From eca09ad89f6c7baf2a2ef9710f92562e574f0c78 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 22 Oct 2024 16:41:29 +0530 Subject: [PATCH 01/62] langfuse use helper for get_langfuse_logging_config --- litellm/integrations/langfuse.py | 55 +++++++++++++++++++++++++- litellm/types/integrations/langfuse.py | 7 ++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 litellm/types/integrations/langfuse.py diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index 885335350d..fa52b3d7a2 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -4,7 +4,7 @@ import copy import inspect import os import traceback -from typing import Optional +from typing import Any, Dict, Optional from packaging.version import Version from pydantic import BaseModel @@ -13,7 +13,8 @@ import litellm from litellm._logging import verbose_logger from litellm.litellm_core_utils.redact_messages import redact_user_api_key_info from litellm.secret_managers.main import str_to_bool -from litellm.types.utils import StandardLoggingPayload +from litellm.types.integrations.langfuse import * +from litellm.types.utils import StandardCallbackDynamicParams, StandardLoggingPayload class LangFuseLogger: @@ -797,3 +798,53 @@ def log_requester_metadata(clean_metadata: dict): returned_metadata.update({"requester_metadata": requester_metadata}) return returned_metadata + + +def get_langfuse_logging_config( + standard_callback_dynamic_params: StandardCallbackDynamicParams, + globalLangfuseLogger: Optional[LangFuseLogger] = None, +) -> LangfuseLoggingConfig: + """ + This function is used to get the Langfuse logging config for the Langfuse Logger. + It checks if the dynamic parameters are provided in the standard_callback_dynamic_params and uses them to get the Langfuse logging config. + + If no dynamic parameters are provided, it uses the default values. + """ + # only use dynamic params if langfuse credentials are passed dynamically + if _dynamic_langfuse_credentials_are_passed(standard_callback_dynamic_params): + return LangfuseLoggingConfig( + langfuse_secret=standard_callback_dynamic_params.get("langfuse_secret") + or standard_callback_dynamic_params.get("langfuse_secret_key"), + langfuse_public_key=standard_callback_dynamic_params.get( + "langfuse_public_key" + ), + langfuse_host=standard_callback_dynamic_params.get("langfuse_host"), + ) + elif globalLangfuseLogger is not None: + return LangfuseLoggingConfig( + langfuse_secret=globalLangfuseLogger.secret_key, + langfuse_public_key=globalLangfuseLogger.public_key, + langfuse_host=globalLangfuseLogger.langfuse_host, + ) + raise ValueError( + "`langfuse` set as a callback but globalLangfuseLogger is not provided and dynamic params are not passed" + ) + + +def _dynamic_langfuse_credentials_are_passed( + standard_callback_dynamic_params: StandardCallbackDynamicParams, +) -> bool: + """ + This function is used to check if the dynamic langfuse credentials are passed in standard_callback_dynamic_params + + Returns: + bool: True if the dynamic langfuse credentials are passed, False otherwise + """ + if ( + standard_callback_dynamic_params.get("langfuse_host") is not None + or standard_callback_dynamic_params.get("langfuse_public_key") is not None + or standard_callback_dynamic_params.get("langfuse_secret") is not None + or standard_callback_dynamic_params.get("langfuse_secret_key") is not None + ): + return True + return False diff --git a/litellm/types/integrations/langfuse.py b/litellm/types/integrations/langfuse.py new file mode 100644 index 0000000000..ecf42d8cd4 --- /dev/null +++ b/litellm/types/integrations/langfuse.py @@ -0,0 +1,7 @@ +from typing import Optional, TypedDict + + +class LangfuseLoggingConfig(TypedDict): + langfuse_secret: Optional[str] + langfuse_public_key: Optional[str] + langfuse_host: Optional[str] From a0c5fee61d9115b8df44a64dda8c4c9fd26b034a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20Ta=C5=9Fk=C3=B6pr=C3=BC?= Date: Tue, 22 Oct 2024 14:11:54 +0300 Subject: [PATCH 02/62] Refactor: apply early return (#6369) --- .../llm_response_utils/get_headers.py | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/litellm/litellm_core_utils/llm_response_utils/get_headers.py b/litellm/litellm_core_utils/llm_response_utils/get_headers.py index 58a5f1715b..cd49b5a4a8 100644 --- a/litellm/litellm_core_utils/llm_response_utils/get_headers.py +++ b/litellm/litellm_core_utils/llm_response_utils/get_headers.py @@ -15,27 +15,28 @@ def get_response_headers(_response_headers: Optional[dict] = None) -> dict: dict: _response_headers with OpenAI headers and llm_provider-{header} """ - if _response_headers is not None: - openai_headers = {} - if "x-ratelimit-limit-requests" in _response_headers: - openai_headers["x-ratelimit-limit-requests"] = _response_headers[ - "x-ratelimit-limit-requests" - ] - if "x-ratelimit-remaining-requests" in _response_headers: - openai_headers["x-ratelimit-remaining-requests"] = _response_headers[ - "x-ratelimit-remaining-requests" - ] - if "x-ratelimit-limit-tokens" in _response_headers: - openai_headers["x-ratelimit-limit-tokens"] = _response_headers[ - "x-ratelimit-limit-tokens" - ] - if "x-ratelimit-remaining-tokens" in _response_headers: - openai_headers["x-ratelimit-remaining-tokens"] = _response_headers[ - "x-ratelimit-remaining-tokens" - ] - llm_provider_headers = _get_llm_provider_headers(_response_headers) - return {**llm_provider_headers, **openai_headers} - return {} + if _response_headers is None: + return {} + + openai_headers = {} + if "x-ratelimit-limit-requests" in _response_headers: + openai_headers["x-ratelimit-limit-requests"] = _response_headers[ + "x-ratelimit-limit-requests" + ] + if "x-ratelimit-remaining-requests" in _response_headers: + openai_headers["x-ratelimit-remaining-requests"] = _response_headers[ + "x-ratelimit-remaining-requests" + ] + if "x-ratelimit-limit-tokens" in _response_headers: + openai_headers["x-ratelimit-limit-tokens"] = _response_headers[ + "x-ratelimit-limit-tokens" + ] + if "x-ratelimit-remaining-tokens" in _response_headers: + openai_headers["x-ratelimit-remaining-tokens"] = _response_headers[ + "x-ratelimit-remaining-tokens" + ] + llm_provider_headers = _get_llm_provider_headers(_response_headers) + return {**llm_provider_headers, **openai_headers} def _get_llm_provider_headers(response_headers: dict) -> dict: From 7a5f997fc956e96d2414be4be3b0a0d68600bf0c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 22 Oct 2024 16:53:25 +0530 Subject: [PATCH 03/62] (refactor) remove berrispendLogger - unused logging integration (#6363) * fix remove berrispendLogger * remove unused clickhouse logger --- cookbook/misc/clickhouse.py | 72 ---- cookbook/misc/clickhouse_insert_logs.py | 39 -- docs/my-website/docs/proxy/configs.md | 5 - litellm/integrations/berrispend.py | 18 - litellm/integrations/clickhouse.py | 334 ------------------ litellm/litellm_core_utils/litellm_logging.py | 72 +--- litellm/utils.py | 4 - tests/local_testing/test_clickhouse_logger.py | 42 --- 8 files changed, 3 insertions(+), 583 deletions(-) delete mode 100644 cookbook/misc/clickhouse.py delete mode 100644 cookbook/misc/clickhouse_insert_logs.py delete mode 100644 litellm/integrations/clickhouse.py delete mode 100644 tests/local_testing/test_clickhouse_logger.py diff --git a/cookbook/misc/clickhouse.py b/cookbook/misc/clickhouse.py deleted file mode 100644 index b40a9346bf..0000000000 --- a/cookbook/misc/clickhouse.py +++ /dev/null @@ -1,72 +0,0 @@ -import clickhouse_connect -import datetime as datetime -import os - -client = clickhouse_connect.get_client( - host=os.getenv("CLICKHOUSE_HOST"), - port=int(os.getenv("CLICKHOUSE_PORT")), - username=os.getenv("CLICKHOUSE_USERNAME"), - password=os.getenv("CLICKHOUSE_PASSWORD"), -) -import clickhouse_connect - -row1 = [ - "ishaan", # request_id - "GET", # call_type - "api_key_123", # api_key - 50.00, # spend - 1000, # total_tokens - 800, # prompt_tokens - 200, # completion_tokens - datetime.datetime.now(), # startTime (replace with the actual timestamp) - datetime.datetime.now(), # endTime (replace with the actual timestamp) - "gpt-3.5", # model - "user123", # user - '{"key": "value"}', # metadata (replace with valid JSON) - "True", # cache_hit - "cache_key_123", # cache_key - "tag1,tag2", # request_tags -] - -row2 = [ - "jaffer", # request_id - "POST", # call_type - "api_key_456", # api_key - 30.50, # spend - 800, # total_tokens - 600, # prompt_tokens - 200, # completion_tokens - datetime.datetime.now(), # startTime (replace with the actual timestamp) - datetime.datetime.now(), # endTime (replace with the actual timestamp) - "gpt-4.0", # model - "user456", # user - '{"key": "value"}', # metadata (replace with valid JSON) - "False", # cache_hit - "cache_key_789", # cache_key - "tag3,tag4", # request_tags -] - -data = [row1, row2] -resp = client.insert( - "spend_logs", - data, - column_names=[ - "request_id", - "call_type", - "api_key", - "spend", - "total_tokens", - "prompt_tokens", - "completion_tokens", - "startTime", - "endTime", - "model", - "user", - "metadata", - "cache_hit", - "cache_key", - "request_tags", - ], -) - -print(resp) diff --git a/cookbook/misc/clickhouse_insert_logs.py b/cookbook/misc/clickhouse_insert_logs.py deleted file mode 100644 index cd1615c938..0000000000 --- a/cookbook/misc/clickhouse_insert_logs.py +++ /dev/null @@ -1,39 +0,0 @@ -# insert data into clickhouse -# response = client.command( -# """ -# CREATE TEMPORARY TABLE temp_spend_logs AS ( -# SELECT -# generateUUIDv4() AS request_id, -# arrayElement(['TypeA', 'TypeB', 'TypeC'], rand() % 3 + 1) AS call_type, -# 'ishaan' as api_key, -# rand() * 1000 AS spend, -# rand() * 100 AS total_tokens, -# rand() * 50 AS prompt_tokens, -# rand() * 50 AS completion_tokens, -# toDate('2024-02-01') + toIntervalDay(rand()%27) AS startTime, -# now() AS endTime, -# arrayElement(['azure/gpt-4', 'gpt-3.5', 'vertexai/gemini-pro', 'mistral/mistral-small', 'ollama/llama2'], rand() % 3 + 1) AS model, -# 'ishaan-insert-rand' as user, -# 'data' as metadata, -# 'true'AS cache_hit, -# 'ishaan' as cache_key, -# '{"tag1": "value1", "tag2": "value2"}' AS request_tags -# FROM numbers(1, 1000000) -# ); -# """ -# ) - -# client.command( -# """ -# -- Insert data into spend_logs table -# INSERT INTO spend_logs -# SELECT * FROM temp_spend_logs; -# """ -# ) - - -# client.command( -# """ -# DROP TABLE IF EXISTS temp_spend_logs; -# """ -# ) diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index 67ad60c282..9070fe068d 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -777,7 +777,6 @@ general_settings: | public_routes | List[str] | (Enterprise Feature) Control list of public routes | | alert_types | List[str] | Control list of alert types to send to slack (Doc on alert types)[./alerting.md] | | enforced_params | List[str] | (Enterprise Feature) List of params that must be included in all requests to the proxy | -| enable_oauth2_auth | boolean | (Enterprise Feature) If true, enables oauth2.0 authentication | | use_x_forwarded_for | str | If true, uses the X-Forwarded-For header to get the client IP address | | service_account_settings | List[Dict[str, Any]] | Set `service_account_settings` if you want to create settings that only apply to service account keys (Doc on service accounts)[./service_accounts.md] | | image_generation_model | str | The default model to use for image generation - ignores model set in request | @@ -898,10 +897,6 @@ router_settings: | BRAINTRUST_API_KEY | API key for Braintrust integration | CIRCLE_OIDC_TOKEN | OpenID Connect token for CircleCI | CIRCLE_OIDC_TOKEN_V2 | Version 2 of the OpenID Connect token for CircleCI -| CLICKHOUSE_HOST | Host for ClickHouse database -| CLICKHOUSE_PASSWORD | Password for ClickHouse authentication -| CLICKHOUSE_PORT | Port for ClickHouse database connection -| CLICKHOUSE_USERNAME | Username for ClickHouse authentication | CONFIG_FILE_PATH | File path for configuration file | CUSTOM_TIKTOKEN_CACHE_DIR | Custom directory for Tiktoken cache | DATABASE_HOST | Hostname for the database server diff --git a/litellm/integrations/berrispend.py b/litellm/integrations/berrispend.py index c26ccd3ca9..a1ad2e6a24 100644 --- a/litellm/integrations/berrispend.py +++ b/litellm/integrations/berrispend.py @@ -84,21 +84,3 @@ model_cost = { "output_cost_per_token": 0.000015, }, } - - -class BerriSpendLogger: - # Class variables or attributes - def __init__(self): - # Instance variables - self.account_id = os.getenv("BERRISPEND_ACCOUNT_ID") - - def price_calculator(self, model, response_obj, start_time, end_time): - return - - def log_event( - self, model, messages, response_obj, start_time, end_time, print_verbose - ): - """ - This integration is not implemented yet. - """ - return diff --git a/litellm/integrations/clickhouse.py b/litellm/integrations/clickhouse.py deleted file mode 100644 index e4f43463f6..0000000000 --- a/litellm/integrations/clickhouse.py +++ /dev/null @@ -1,334 +0,0 @@ -# callback to make a request to an API endpoint - -#### What this does #### -# On success, logs events to Promptlayer -import datetime -import json -import os -import traceback -from typing import Literal, Optional, Union - -import dotenv -import requests - -import litellm -from litellm._logging import verbose_logger -from litellm.caching.caching import DualCache -from litellm.proxy._types import UserAPIKeyAuth -from litellm.types.utils import StandardLoggingPayload - -#### What this does #### -# On success + failure, log events to Supabase - - -def create_client(): - try: - import clickhouse_connect - - port = os.getenv("CLICKHOUSE_PORT") - clickhouse_host = os.getenv("CLICKHOUSE_HOST") - if clickhouse_host is not None: - verbose_logger.debug("setting up clickhouse") - - port = os.getenv("CLICKHOUSE_PORT") - if port is not None and isinstance(port, str): - port = int(port) - - host: Optional[str] = os.getenv("CLICKHOUSE_HOST") - if host is None: - raise ValueError("CLICKHOUSE_HOST is not set") - - username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME") - if username is None: - raise ValueError("CLICKHOUSE_USERNAME is not set") - - password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD") - if password is None: - raise ValueError("CLICKHOUSE_PASSWORD is not set") - if port is None: - raise ValueError("CLICKHOUSE_PORT is not set") - - client = clickhouse_connect.get_client( - host=host, - port=port, - username=username, - password=password, - ) - return client - else: - raise Exception("Clickhouse: Clickhouse host not set") - except Exception as e: - raise ValueError(f"Clickhouse: {e}") - - -def build_daily_metrics(): - click_house_client = create_client() - - # get daily spend - daily_spend = click_house_client.query_df( - """ - SELECT sumMerge(DailySpend) as daily_spend, day FROM daily_aggregated_spend GROUP BY day - """ - ) - - # get daily spend per model - daily_spend_per_model = click_house_client.query_df( - """ - SELECT sumMerge(DailySpend) as daily_spend, day, model FROM daily_aggregated_spend_per_model GROUP BY day, model - """ - ) - new_df = daily_spend_per_model.to_dict(orient="records") - import pandas as pd - - df = pd.DataFrame(new_df) - # Group by 'day' and create a dictionary for each group - result_dict = {} - for day, group in df.groupby("day"): - models = group["model"].tolist() - spend = group["daily_spend"].tolist() - spend_per_model = {model: spend for model, spend in zip(models, spend)} - result_dict[day] = spend_per_model - - # Display the resulting dictionary - - # get daily spend per API key - daily_spend_per_api_key = click_house_client.query_df( - """ - SELECT - daily_spend, - day, - api_key - FROM ( - SELECT - sumMerge(DailySpend) as daily_spend, - day, - api_key, - RANK() OVER (PARTITION BY day ORDER BY sumMerge(DailySpend) DESC) as spend_rank - FROM - daily_aggregated_spend_per_api_key - GROUP BY - day, - api_key - ) AS ranked_api_keys - WHERE - spend_rank <= 5 - AND day IS NOT NULL - ORDER BY - day, - daily_spend DESC - """ - ) - new_df = daily_spend_per_api_key.to_dict(orient="records") - import pandas as pd - - df = pd.DataFrame(new_df) - # Group by 'day' and create a dictionary for each group - api_key_result_dict = {} - for day, group in df.groupby("day"): - api_keys = group["api_key"].tolist() - spend = group["daily_spend"].tolist() - spend_per_api_key = {api_key: spend for api_key, spend in zip(api_keys, spend)} - api_key_result_dict[day] = spend_per_api_key - - # Display the resulting dictionary - - # Calculate total spend across all days - total_spend = daily_spend["daily_spend"].sum() - - # Identify top models and top API keys with the highest spend across all days - top_models = {} - top_api_keys = {} - - for day, spend_per_model in result_dict.items(): - for model, model_spend in spend_per_model.items(): - if model not in top_models or model_spend > top_models[model]: - top_models[model] = model_spend - - for day, spend_per_api_key in api_key_result_dict.items(): - for api_key, api_key_spend in spend_per_api_key.items(): - if api_key not in top_api_keys or api_key_spend > top_api_keys[api_key]: - top_api_keys[api_key] = api_key_spend - - # for each day in daily spend, look up the day in result_dict and api_key_result_dict - # Assuming daily_spend DataFrame has 'day' column - result = [] - for index, row in daily_spend.iterrows(): - day = row["day"] - data_day = row.to_dict() - - # Look up in result_dict - if day in result_dict: - spend_per_model = result_dict[day] - # Assuming there is a column named 'model' in daily_spend - data_day["spend_per_model"] = spend_per_model # Assign 0 if model not found - - # Look up in api_key_result_dict - if day in api_key_result_dict: - spend_per_api_key = api_key_result_dict[day] - # Assuming there is a column named 'api_key' in daily_spend - data_day["spend_per_api_key"] = spend_per_api_key - - result.append(data_day) - - data_to_return = {} - data_to_return["daily_spend"] = result - - data_to_return["total_spend"] = total_spend - data_to_return["top_models"] = top_models - data_to_return["top_api_keys"] = top_api_keys - return data_to_return - - -# build_daily_metrics() - - -def _start_clickhouse(): - import clickhouse_connect - - port = os.getenv("CLICKHOUSE_PORT") - clickhouse_host = os.getenv("CLICKHOUSE_HOST") - if clickhouse_host is not None: - verbose_logger.debug("setting up clickhouse") - if port is not None and isinstance(port, str): - port = int(port) - - port = os.getenv("CLICKHOUSE_PORT") - if port is not None and isinstance(port, str): - port = int(port) - - host: Optional[str] = os.getenv("CLICKHOUSE_HOST") - if host is None: - raise ValueError("CLICKHOUSE_HOST is not set") - - username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME") - if username is None: - raise ValueError("CLICKHOUSE_USERNAME is not set") - - password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD") - if password is None: - raise ValueError("CLICKHOUSE_PASSWORD is not set") - if port is None: - raise ValueError("CLICKHOUSE_PORT is not set") - - client = clickhouse_connect.get_client( - host=host, - port=port, - username=username, - password=password, - ) - # view all tables in DB - response = client.query("SHOW TABLES") - verbose_logger.debug( - f"checking if litellm spend logs exists, all tables={response.result_rows}" - ) - # all tables is returned like this: all tables = [('new_table',), ('spend_logs',)] - # check if spend_logs in all tables - table_names = [all_tables[0] for all_tables in response.result_rows] - - if "spend_logs" not in table_names: - verbose_logger.debug( - "Clickhouse: spend logs table does not exist... creating it" - ) - - response = client.command( - """ - CREATE TABLE default.spend_logs - ( - `request_id` String, - `call_type` String, - `api_key` String, - `spend` Float64, - `total_tokens` Int256, - `prompt_tokens` Int256, - `completion_tokens` Int256, - `startTime` DateTime, - `endTime` DateTime, - `model` String, - `user` String, - `metadata` String, - `cache_hit` String, - `cache_key` String, - `request_tags` String - ) - ENGINE = MergeTree - ORDER BY tuple(); - """ - ) - else: - # check if spend logs exist, if it does then return the schema - response = client.query("DESCRIBE default.spend_logs") - verbose_logger.debug(f"spend logs schema ={response.result_rows}") - - -class ClickhouseLogger: - # Class variables or attributes - def __init__(self, endpoint=None, headers=None): - import clickhouse_connect - - _start_clickhouse() - - verbose_logger.debug( - f"ClickhouseLogger init, host {os.getenv('CLICKHOUSE_HOST')}, port {os.getenv('CLICKHOUSE_PORT')}, username {os.getenv('CLICKHOUSE_USERNAME')}" - ) - - port = os.getenv("CLICKHOUSE_PORT") - if port is not None and isinstance(port, str): - port = int(port) - - host: Optional[str] = os.getenv("CLICKHOUSE_HOST") - if host is None: - raise ValueError("CLICKHOUSE_HOST is not set") - - username: Optional[str] = os.getenv("CLICKHOUSE_USERNAME") - if username is None: - raise ValueError("CLICKHOUSE_USERNAME is not set") - - password: Optional[str] = os.getenv("CLICKHOUSE_PASSWORD") - if password is None: - raise ValueError("CLICKHOUSE_PASSWORD is not set") - if port is None: - raise ValueError("CLICKHOUSE_PORT is not set") - - client = clickhouse_connect.get_client( - host=host, - port=port, - username=username, - password=password, - ) - self.client = client - - # This is sync, because we run this in a separate thread. Running in a sepearate thread ensures it will never block an LLM API call - # Experience with s3, Langfuse shows that async logging events are complicated and can block LLM calls - def log_event( - self, kwargs, response_obj, start_time, end_time, user_id, print_verbose - ): - try: - verbose_logger.debug( - f"ClickhouseLogger Logging - Enters logging function for model {kwargs}" - ) - # follows the same params as langfuse.py - - payload: Optional[StandardLoggingPayload] = kwargs.get( - "standard_logging_object" - ) - if payload is None: - return - # Build the initial payload - - verbose_logger.debug(f"\nClickhouse Logger - Logging payload = {payload}") - - # just get the payload items in one array and payload keys in 2nd array - values = [] - keys = [] - for key, value in payload.items(): - keys.append(key) - values.append(value) - data = [values] - - response = self.client.insert("default.spend_logs", data, column_names=keys) - - # make request to endpoint with payload - verbose_logger.debug(f"Clickhouse Logger - final response = {response}") - except Exception as e: - verbose_logger.debug(f"Clickhouse - {str(e)}\n{traceback.format_exc()}") - pass diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index d17b2c7908..f41ac256bb 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -61,9 +61,7 @@ from litellm.utils import ( from ..integrations.aispend import AISpendLogger from ..integrations.argilla import ArgillaLogger from ..integrations.athina import AthinaLogger -from ..integrations.berrispend import BerriSpendLogger from ..integrations.braintrust_logging import BraintrustLogger -from ..integrations.clickhouse import ClickhouseLogger from ..integrations.datadog.datadog import DataDogLogger from ..integrations.dynamodb import DyanmoDBLogger from ..integrations.galileo import GalileoObserve @@ -122,13 +120,10 @@ prometheusLogger = None dynamoLogger = None s3Logger = None genericAPILogger = None -clickHouseLogger = None greenscaleLogger = None lunaryLogger = None aispendLogger = None -berrispendLogger = None supabaseClient = None -liteDebuggerClient = None callback_list: Optional[List[str]] = [] user_logger_fn = None additional_details: Optional[Dict[str, str]] = {} @@ -191,7 +186,7 @@ in_memory_dynamic_logger_cache = DynamicLoggingCache() class Logging: - global supabaseClient, liteDebuggerClient, promptLayerLogger, weightsBiasesLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app + global supabaseClient, promptLayerLogger, weightsBiasesLogger, logfireLogger, capture_exception, add_breadcrumb, lunaryLogger, logfireLogger, prometheusLogger, slack_app custom_pricing: bool = False stream_options = None @@ -970,22 +965,6 @@ class Logging: ): print_verbose("no-log request, skipping logging") continue - if callback == "lite_debugger" and liteDebuggerClient is not None: - print_verbose("reaches lite_debugger for logging!") - print_verbose(f"liteDebuggerClient: {liteDebuggerClient}") - print_verbose( - f"liteDebuggerClient details function {self.call_type} and stream set to {self.stream}" - ) - liteDebuggerClient.log_event( - end_user=kwargs.get("user", "default"), - response_obj=result, - start_time=start_time, - end_time=end_time, - litellm_call_id=self.litellm_call_id, - print_verbose=print_verbose, - call_type=self.call_type, - stream=self.stream, - ) if callback == "promptlayer" and promptLayerLogger is not None: print_verbose("reaches promptlayer for logging!") promptLayerLogger.log_event( @@ -1248,37 +1227,6 @@ class Logging: user_id=kwargs.get("user", None), print_verbose=print_verbose, ) - if callback == "clickhouse": - global clickHouseLogger - verbose_logger.debug("reaches clickhouse for success logging!") - kwargs = {} - for k, v in self.model_call_details.items(): - if ( - k != "original_response" - ): # copy.deepcopy raises errors as this could be a coroutine - kwargs[k] = v - # this only logs streaming once, complete_streaming_response exists i.e when stream ends - if self.stream: - verbose_logger.debug( - f"is complete_streaming_response in kwargs: {kwargs.get('complete_streaming_response', None)}" - ) - if complete_streaming_response is None: - continue - else: - print_verbose( - "reaches clickhouse for streaming logging!" - ) - result = kwargs["complete_streaming_response"] - if clickHouseLogger is None: - clickHouseLogger = ClickhouseLogger() - clickHouseLogger.log_event( - kwargs=kwargs, - response_obj=result, - start_time=start_time, - end_time=end_time, - user_id=kwargs.get("user", None), - print_verbose=print_verbose, - ) if callback == "greenscale" and greenscaleLogger is not None: kwargs = {} for k, v in self.model_call_details.items(): @@ -1874,9 +1822,7 @@ class Logging: ) for callback in callbacks: try: - if callback == "lite_debugger" and liteDebuggerClient is not None: - pass - elif callback == "lunary" and lunaryLogger is not None: + if callback == "lunary" and lunaryLogger is not None: print_verbose("reaches lunary for logging error!") model = self.model @@ -2195,7 +2141,7 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915 """ Globally sets the callback client """ - global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, aispendLogger, berrispendLogger, supabaseClient, liteDebuggerClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger + global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, aispendLogger, supabaseClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger try: for callback in callback_list: @@ -2277,24 +2223,12 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915 logfireLogger = LogfireLogger() elif callback == "aispend": aispendLogger = AISpendLogger() - elif callback == "berrispend": - berrispendLogger = BerriSpendLogger() elif callback == "supabase": print_verbose("instantiating supabase") supabaseClient = Supabase() elif callback == "greenscale": greenscaleLogger = GreenscaleLogger() print_verbose("Initialized Greenscale Logger") - elif callback == "lite_debugger": - print_verbose("instantiating lite_debugger") - if function_id: - liteDebuggerClient = LiteDebugger(email=function_id) - elif litellm.token: - liteDebuggerClient = LiteDebugger(email=litellm.token) - elif litellm.email: - liteDebuggerClient = LiteDebugger(email=litellm.email) - else: - liteDebuggerClient = LiteDebugger(email=str(uuid.uuid4())) elif callable(callback): customLogger = CustomLogger() except Exception as e: diff --git a/litellm/utils.py b/litellm/utils.py index c4525ad7c3..e82a937bb4 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -213,13 +213,10 @@ prometheusLogger = None dynamoLogger = None s3Logger = None genericAPILogger = None -clickHouseLogger = None greenscaleLogger = None lunaryLogger = None aispendLogger = None -berrispendLogger = None supabaseClient = None -liteDebuggerClient = None callback_list: Optional[List[str]] = [] user_logger_fn = None additional_details: Optional[Dict[str, str]] = {} @@ -609,7 +606,6 @@ def function_setup( # noqa: PLR0915 def client(original_function): # noqa: PLR0915 - global liteDebuggerClient rules_obj = Rules() def check_coroutine(value) -> bool: diff --git a/tests/local_testing/test_clickhouse_logger.py b/tests/local_testing/test_clickhouse_logger.py deleted file mode 100644 index 02b236999d..0000000000 --- a/tests/local_testing/test_clickhouse_logger.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys -import os -import io, asyncio - -# import logging -# logging.basicConfig(level=logging.DEBUG) -sys.path.insert(0, os.path.abspath("../..")) -print("Modified sys.path:", sys.path) - - -from litellm import completion -import litellm -from litellm._logging import verbose_logger -import logging - -litellm.num_retries = 3 - -import time, random -import pytest - - -@pytest.mark.asyncio -@pytest.mark.skip(reason="beta test - this is a new feature") -async def test_custom_api_logging(): - try: - litellm.success_callback = ["clickhouse"] - litellm.set_verbose = True - verbose_logger.setLevel(logging.DEBUG) - await litellm.acompletion( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": f"This is a test"}], - max_tokens=10, - temperature=0.7, - user="ishaan-2", - ) - - except Exception as e: - pytest.fail(f"An exception occurred - {e}") - finally: - # post, close log file and verify - # Reset stdout to the original value - print("Passed!") From 7853cb791de84a1a76af20b9b95ea79e415540b0 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 22 Oct 2024 18:16:47 +0530 Subject: [PATCH 04/62] fix docs configs.md --- docs/my-website/docs/proxy/configs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index 9070fe068d..aa79242d4b 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -777,6 +777,7 @@ general_settings: | public_routes | List[str] | (Enterprise Feature) Control list of public routes | | alert_types | List[str] | Control list of alert types to send to slack (Doc on alert types)[./alerting.md] | | enforced_params | List[str] | (Enterprise Feature) List of params that must be included in all requests to the proxy | +| enable_oauth2_auth | boolean | (Enterprise Feature) If true, enables oauth2.0 authentication | | use_x_forwarded_for | str | If true, uses the X-Forwarded-For header to get the client IP address | | service_account_settings | List[Dict[str, Any]] | Set `service_account_settings` if you want to create settings that only apply to service account keys (Doc on service accounts)[./service_accounts.md] | | image_generation_model | str | The default model to use for image generation - ignores model set in request | From 8359cb6fa9bf7b0bf4f3df630cf8666adffa2813 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 22 Oct 2024 19:13:19 +0530 Subject: [PATCH 05/62] (fix) standard logging metadata + add unit testing (#6366) * fix setting StandardLoggingMetadata * add unit testing for standard logging metadata * fix otel logging test * fix linting * fix typing --- litellm/litellm_core_utils/litellm_logging.py | 12 +-- .../test_otel_logging.py | 8 ++ .../test_standard_logging_payload.py | 86 +++++++++++++++++++ 3 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 tests/logging_callback_tests/test_standard_logging_payload.py diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index f41ac256bb..f1803043c3 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2801,14 +2801,10 @@ def get_standard_logging_metadata( ) if isinstance(metadata, dict): # Filter the metadata dictionary to include only the specified keys - clean_metadata = StandardLoggingMetadata( - **{ # type: ignore - key: metadata[key] - for key in StandardLoggingMetadata.__annotations__.keys() - if key in metadata - } - ) - + supported_keys = StandardLoggingMetadata.__annotations__.keys() + for key in supported_keys: + if key in metadata: + clean_metadata[key] = metadata[key] # type: ignore if metadata.get("user_api_key") is not None: if is_valid_sha256_hash(str(metadata.get("user_api_key"))): clean_metadata["user_api_key_hash"] = metadata.get( diff --git a/tests/logging_callback_tests/test_otel_logging.py b/tests/logging_callback_tests/test_otel_logging.py index 49212607b8..14cfa1c139 100644 --- a/tests/logging_callback_tests/test_otel_logging.py +++ b/tests/logging_callback_tests/test_otel_logging.py @@ -260,6 +260,14 @@ def validate_redacted_message_span_attributes(span): "llm.usage.total_tokens", "gen_ai.usage.completion_tokens", "gen_ai.usage.prompt_tokens", + "metadata.user_api_key_hash", + "metadata.requester_ip_address", + "metadata.user_api_key_team_alias", + "metadata.requester_metadata", + "metadata.user_api_key_team_id", + "metadata.spend_logs_metadata", + "metadata.user_api_key_alias", + "metadata.user_api_key_user_id", ] _all_attributes = set([name for name in span.attributes.keys()]) diff --git a/tests/logging_callback_tests/test_standard_logging_payload.py b/tests/logging_callback_tests/test_standard_logging_payload.py new file mode 100644 index 0000000000..7ae3ae6ede --- /dev/null +++ b/tests/logging_callback_tests/test_standard_logging_payload.py @@ -0,0 +1,86 @@ +import json +import os +import sys +from datetime import datetime +from unittest.mock import AsyncMock + +from pydantic.main import Model + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system-path + +from typing import Literal + +import pytest +import litellm +import asyncio +import logging +from litellm.litellm_core_utils.litellm_logging import ( + get_standard_logging_metadata, + StandardLoggingMetadata, +) + + +def all_fields_present(standard_logging_metadata: StandardLoggingMetadata): + for field in StandardLoggingMetadata.__annotations__.keys(): + assert field in standard_logging_metadata + + +@pytest.mark.parametrize( + "metadata_key, metadata_value", + [ + ("user_api_key_alias", "test_alias"), + ("user_api_key_hash", "test_hash"), + ("user_api_key_team_id", "test_team_id"), + ("user_api_key_user_id", "test_user_id"), + ("user_api_key_team_alias", "test_team_alias"), + ("spend_logs_metadata", {"key": "value"}), + ("requester_ip_address", "127.0.0.1"), + ("requester_metadata", {"user_agent": "test_agent"}), + ], +) +def test_get_standard_logging_metadata(metadata_key, metadata_value): + """ + Test that the get_standard_logging_metadata function correctly sets the metadata fields. + + All fields in StandardLoggingMetadata should ALWAYS be present. + """ + metadata = {metadata_key: metadata_value} + standard_logging_metadata = get_standard_logging_metadata(metadata) + + print("standard_logging_metadata", standard_logging_metadata) + + # Assert that all fields in StandardLoggingMetadata are present + all_fields_present(standard_logging_metadata) + + # Assert that the specific metadata field is set correctly + assert standard_logging_metadata[metadata_key] == metadata_value + + +def test_get_standard_logging_metadata_user_api_key_hash(): + valid_hash = "a" * 64 # 64 character string + metadata = {"user_api_key": valid_hash} + result = get_standard_logging_metadata(metadata) + assert result["user_api_key_hash"] == valid_hash + + +def test_get_standard_logging_metadata_invalid_user_api_key(): + invalid_hash = "not_a_valid_hash" + metadata = {"user_api_key": invalid_hash} + result = get_standard_logging_metadata(metadata) + all_fields_present(result) + assert result["user_api_key_hash"] is None + + +def test_get_standard_logging_metadata_invalid_keys(): + metadata = { + "user_api_key_alias": "test_alias", + "invalid_key": "should_be_ignored", + "another_invalid_key": 123, + } + result = get_standard_logging_metadata(metadata) + all_fields_present(result) + assert result["user_api_key_alias"] == "test_alias" + assert "invalid_key" not in result + assert "another_invalid_key" not in result From 400cbff9ba23ed7ce6a7a368fc9e7868ea296bb0 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 22 Oct 2024 23:20:01 +0530 Subject: [PATCH 06/62] Revert "(fix) standard logging metadata + add unit testing (#6366)" (#6381) This reverts commit 8359cb6fa9bf7b0bf4f3df630cf8666adffa2813. --- litellm/litellm_core_utils/litellm_logging.py | 12 ++- .../test_otel_logging.py | 8 -- .../test_standard_logging_payload.py | 86 ------------------- 3 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 tests/logging_callback_tests/test_standard_logging_payload.py diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index f1803043c3..f41ac256bb 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2801,10 +2801,14 @@ def get_standard_logging_metadata( ) if isinstance(metadata, dict): # Filter the metadata dictionary to include only the specified keys - supported_keys = StandardLoggingMetadata.__annotations__.keys() - for key in supported_keys: - if key in metadata: - clean_metadata[key] = metadata[key] # type: ignore + clean_metadata = StandardLoggingMetadata( + **{ # type: ignore + key: metadata[key] + for key in StandardLoggingMetadata.__annotations__.keys() + if key in metadata + } + ) + if metadata.get("user_api_key") is not None: if is_valid_sha256_hash(str(metadata.get("user_api_key"))): clean_metadata["user_api_key_hash"] = metadata.get( diff --git a/tests/logging_callback_tests/test_otel_logging.py b/tests/logging_callback_tests/test_otel_logging.py index 14cfa1c139..49212607b8 100644 --- a/tests/logging_callback_tests/test_otel_logging.py +++ b/tests/logging_callback_tests/test_otel_logging.py @@ -260,14 +260,6 @@ def validate_redacted_message_span_attributes(span): "llm.usage.total_tokens", "gen_ai.usage.completion_tokens", "gen_ai.usage.prompt_tokens", - "metadata.user_api_key_hash", - "metadata.requester_ip_address", - "metadata.user_api_key_team_alias", - "metadata.requester_metadata", - "metadata.user_api_key_team_id", - "metadata.spend_logs_metadata", - "metadata.user_api_key_alias", - "metadata.user_api_key_user_id", ] _all_attributes = set([name for name in span.attributes.keys()]) diff --git a/tests/logging_callback_tests/test_standard_logging_payload.py b/tests/logging_callback_tests/test_standard_logging_payload.py deleted file mode 100644 index 7ae3ae6ede..0000000000 --- a/tests/logging_callback_tests/test_standard_logging_payload.py +++ /dev/null @@ -1,86 +0,0 @@ -import json -import os -import sys -from datetime import datetime -from unittest.mock import AsyncMock - -from pydantic.main import Model - -sys.path.insert( - 0, os.path.abspath("../..") -) # Adds the parent directory to the system-path - -from typing import Literal - -import pytest -import litellm -import asyncio -import logging -from litellm.litellm_core_utils.litellm_logging import ( - get_standard_logging_metadata, - StandardLoggingMetadata, -) - - -def all_fields_present(standard_logging_metadata: StandardLoggingMetadata): - for field in StandardLoggingMetadata.__annotations__.keys(): - assert field in standard_logging_metadata - - -@pytest.mark.parametrize( - "metadata_key, metadata_value", - [ - ("user_api_key_alias", "test_alias"), - ("user_api_key_hash", "test_hash"), - ("user_api_key_team_id", "test_team_id"), - ("user_api_key_user_id", "test_user_id"), - ("user_api_key_team_alias", "test_team_alias"), - ("spend_logs_metadata", {"key": "value"}), - ("requester_ip_address", "127.0.0.1"), - ("requester_metadata", {"user_agent": "test_agent"}), - ], -) -def test_get_standard_logging_metadata(metadata_key, metadata_value): - """ - Test that the get_standard_logging_metadata function correctly sets the metadata fields. - - All fields in StandardLoggingMetadata should ALWAYS be present. - """ - metadata = {metadata_key: metadata_value} - standard_logging_metadata = get_standard_logging_metadata(metadata) - - print("standard_logging_metadata", standard_logging_metadata) - - # Assert that all fields in StandardLoggingMetadata are present - all_fields_present(standard_logging_metadata) - - # Assert that the specific metadata field is set correctly - assert standard_logging_metadata[metadata_key] == metadata_value - - -def test_get_standard_logging_metadata_user_api_key_hash(): - valid_hash = "a" * 64 # 64 character string - metadata = {"user_api_key": valid_hash} - result = get_standard_logging_metadata(metadata) - assert result["user_api_key_hash"] == valid_hash - - -def test_get_standard_logging_metadata_invalid_user_api_key(): - invalid_hash = "not_a_valid_hash" - metadata = {"user_api_key": invalid_hash} - result = get_standard_logging_metadata(metadata) - all_fields_present(result) - assert result["user_api_key_hash"] is None - - -def test_get_standard_logging_metadata_invalid_keys(): - metadata = { - "user_api_key_alias": "test_alias", - "invalid_key": "should_be_ignored", - "another_invalid_key": 123, - } - result = get_standard_logging_metadata(metadata) - all_fields_present(result) - assert result["user_api_key_alias"] == "test_alias" - assert "invalid_key" not in result - assert "another_invalid_key" not in result From 21ace6de45523c7424fa18f5ababb91eeb998748 Mon Sep 17 00:00:00 2001 From: Low Jian Sheng <15527690+lowjiansheng@users.noreply.github.com> Date: Wed, 23 Oct 2024 02:09:20 +0800 Subject: [PATCH 07/62] add new 35 mode lcard (#6378) --- model_prices_and_context_window.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 6944cdd300..4f221d58ab 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -1751,6 +1751,22 @@ "supports_assistant_prefill": true, "supports_prompt_caching": true }, + "claude-3-5-sonnet-20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "cache_creation_input_token_cost": 0.00000375, + "cache_read_input_token_cost": 0.0000003, + "litellm_provider": "anthropic", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "tool_use_system_prompt_tokens": 159, + "supports_assistant_prefill": true, + "supports_prompt_caching": true + }, "text-bison": { "max_tokens": 2048, "max_input_tokens": 8192, From 7939e930ff5b6451a03f1a7ee7c6a92e0704568c Mon Sep 17 00:00:00 2001 From: David Manouchehri Date: Tue, 22 Oct 2024 13:51:16 -0500 Subject: [PATCH 08/62] Add claude 3 5 sonnet 20241022 models for all provides (#6380) * Add Claude 3.5 v2 on Amazon Bedrock and Vertex AI. * added anthropic/claude-3-5-sonnet-20241022 * add new 35 mode lcard --------- Co-authored-by: Paul Gauthier Co-authored-by: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> --- litellm/llms/bedrock/chat/converse_handler.py | 1 + model_prices_and_context_window.json | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/litellm/llms/bedrock/chat/converse_handler.py b/litellm/llms/bedrock/chat/converse_handler.py index 305bd86cee..b775cc64cb 100644 --- a/litellm/llms/bedrock/chat/converse_handler.py +++ b/litellm/llms/bedrock/chat/converse_handler.py @@ -19,6 +19,7 @@ from ..common_utils import BedrockError from .invoke_handler import AWSEventStreamDecoder, MockResponseIterator, make_call BEDROCK_CONVERSE_MODELS = [ + "anthropic.claude-3-5-sonnet-20241022-v2:0", "anthropic.claude-3-5-sonnet-20240620-v1:0", "anthropic.claude-3-opus-20240229-v1:0", "anthropic.claude-3-sonnet-20240229-v1:0", diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 4f221d58ab..e637418049 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -2594,6 +2594,18 @@ "supports_vision": true, "supports_assistant_prefill": true }, + "vertex_ai/claude-3-5-sonnet-v2@20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "vertex_ai-anthropic_models", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_assistant_prefill": true + }, "vertex_ai/claude-3-haiku@20240307": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -3588,6 +3600,22 @@ "supports_vision": true, "tool_use_system_prompt_tokens": 264 }, + "anthropic/claude-3-5-sonnet-20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "cache_creation_input_token_cost": 0.00000375, + "cache_read_input_token_cost": 0.0000003, + "litellm_provider": "anthropic", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "tool_use_system_prompt_tokens": 159, + "supports_assistant_prefill": true, + "supports_prompt_caching": true + }, "openrouter/anthropic/claude-3.5-sonnet": { "max_tokens": 8192, "max_input_tokens": 200000, @@ -4262,6 +4290,17 @@ "supports_function_calling": true, "supports_vision": true }, + "anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -4306,6 +4345,17 @@ "supports_function_calling": true, "supports_vision": true }, + "us.anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "us.anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -4350,6 +4400,17 @@ "supports_function_calling": true, "supports_vision": true }, + "eu.anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "eu.anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, From 24a0d26eb16fc9de8a6eeee6d639fd90efb78c5a Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Tue, 22 Oct 2024 12:06:25 -0700 Subject: [PATCH 09/62] test(skip-flaky-google-context-caching-test): google is not reliable. their sample code is also not working --- tests/pass_through_tests/test_vertex_ai.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/pass_through_tests/test_vertex_ai.py b/tests/pass_through_tests/test_vertex_ai.py index d0c5088d92..32d6515b89 100644 --- a/tests/pass_through_tests/test_vertex_ai.py +++ b/tests/pass_through_tests/test_vertex_ai.py @@ -154,6 +154,9 @@ async def test_basic_vertex_ai_pass_through_streaming_with_spendlog(): pass +@pytest.mark.skip( + reason="skip flaky test - google context caching is flaky and not reliable." +) @pytest.mark.asyncio async def test_vertex_ai_pass_through_endpoint_context_caching(): import vertexai @@ -161,7 +164,7 @@ async def test_vertex_ai_pass_through_endpoint_context_caching(): from vertexai.preview import caching import datetime - load_vertex_ai_credentials() + # load_vertex_ai_credentials() vertexai.init( project="adroit-crow-413218", From f943410e326192582b587384f2da0041be526490 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Tue, 22 Oct 2024 13:33:29 -0700 Subject: [PATCH 10/62] test(test_alangfuse.py): handle flaky langfuse test better --- ...odel_prices_and_context_window_backup.json | 77 +++++++++++++++++++ tests/local_testing/test_alangfuse.py | 7 +- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 6944cdd300..e637418049 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -1751,6 +1751,22 @@ "supports_assistant_prefill": true, "supports_prompt_caching": true }, + "claude-3-5-sonnet-20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "cache_creation_input_token_cost": 0.00000375, + "cache_read_input_token_cost": 0.0000003, + "litellm_provider": "anthropic", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "tool_use_system_prompt_tokens": 159, + "supports_assistant_prefill": true, + "supports_prompt_caching": true + }, "text-bison": { "max_tokens": 2048, "max_input_tokens": 8192, @@ -2578,6 +2594,18 @@ "supports_vision": true, "supports_assistant_prefill": true }, + "vertex_ai/claude-3-5-sonnet-v2@20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "vertex_ai-anthropic_models", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "supports_assistant_prefill": true + }, "vertex_ai/claude-3-haiku@20240307": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -3572,6 +3600,22 @@ "supports_vision": true, "tool_use_system_prompt_tokens": 264 }, + "anthropic/claude-3-5-sonnet-20241022": { + "max_tokens": 8192, + "max_input_tokens": 200000, + "max_output_tokens": 8192, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "cache_creation_input_token_cost": 0.00000375, + "cache_read_input_token_cost": 0.0000003, + "litellm_provider": "anthropic", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true, + "tool_use_system_prompt_tokens": 159, + "supports_assistant_prefill": true, + "supports_prompt_caching": true + }, "openrouter/anthropic/claude-3.5-sonnet": { "max_tokens": 8192, "max_input_tokens": 200000, @@ -4246,6 +4290,17 @@ "supports_function_calling": true, "supports_vision": true }, + "anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -4290,6 +4345,17 @@ "supports_function_calling": true, "supports_vision": true }, + "us.anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "us.anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, @@ -4334,6 +4400,17 @@ "supports_function_calling": true, "supports_vision": true }, + "eu.anthropic.claude-3-5-sonnet-20241022-v2:0": { + "max_tokens": 4096, + "max_input_tokens": 200000, + "max_output_tokens": 4096, + "input_cost_per_token": 0.000003, + "output_cost_per_token": 0.000015, + "litellm_provider": "bedrock", + "mode": "chat", + "supports_function_calling": true, + "supports_vision": true + }, "eu.anthropic.claude-3-haiku-20240307-v1:0": { "max_tokens": 4096, "max_input_tokens": 200000, diff --git a/tests/local_testing/test_alangfuse.py b/tests/local_testing/test_alangfuse.py index e9da35b770..e4c1393785 100644 --- a/tests/local_testing/test_alangfuse.py +++ b/tests/local_testing/test_alangfuse.py @@ -432,7 +432,12 @@ async def test_aaalangfuse_logging_metadata(langfuse_client): # Tests the metadata filtering and the override of the output to be the last generation for trace_id, generation_ids in trace_identifiers.items(): - trace = langfuse_client.get_trace(id=trace_id) + try: + trace = langfuse_client.get_trace(id=trace_id) + except Exception as e: + if "Trace not found within authorized project" in str(e): + print(f"Trace {trace_id} not found") + continue assert trace.id == trace_id assert trace.session_id == session_id assert trace.metadata != trace_metadata From b75019c1a502f9af60f8387effb822763233a5d4 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 09:38:35 +0530 Subject: [PATCH 11/62] (feat) Arize - Allow using Arize HTTP endpoint (#6364) * arize use helper for get_arize_opentelemetry_config * use helper to get Arize OTEL config * arize add helpers for arize * docs allow using arize http endpoint * fix importing OTEL for Arize * use static methods for ArizeLogger * fix ArizeLogger tests --- .../docs/observability/arize_integration.md | 3 +- docs/my-website/docs/proxy/logging.md | 3 +- litellm/integrations/arize_ai.py | 287 +++++++++++------- litellm/integrations/opentelemetry.py | 4 +- litellm/litellm_core_utils/litellm_logging.py | 18 +- litellm/types/integrations/arize.py | 10 + tests/local_testing/test_arize_ai.py | 56 +++- 7 files changed, 257 insertions(+), 124 deletions(-) create mode 100644 litellm/types/integrations/arize.py diff --git a/docs/my-website/docs/observability/arize_integration.md b/docs/my-website/docs/observability/arize_integration.md index 17be003f8a..a69d32e5b3 100644 --- a/docs/my-website/docs/observability/arize_integration.md +++ b/docs/my-website/docs/observability/arize_integration.md @@ -62,7 +62,8 @@ litellm_settings: environment_variables: ARIZE_SPACE_KEY: "d0*****" ARIZE_API_KEY: "141a****" - ARIZE_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize api endpoint + ARIZE_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize GRPC api endpoint + ARIZE_HTTP_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize HTTP api endpoint. Set either this or ARIZE_ENDPOINT ``` ## Support & Talk to Founders diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 3a764005fe..72c2e3773b 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -1279,7 +1279,8 @@ litellm_settings: environment_variables: ARIZE_SPACE_KEY: "d0*****" ARIZE_API_KEY: "141a****" - ARIZE_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize api endpoint + ARIZE_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize GRPC api endpoint + ARIZE_HTTP_ENDPOINT: "https://otlp.arize.com/v1" # OPTIONAL - your custom arize HTTP api endpoint. Set either this or ARIZE_ENDPOINT ``` 2. Start Proxy diff --git a/litellm/integrations/arize_ai.py b/litellm/integrations/arize_ai.py index 5a66cfd0ca..acd3f745bd 100644 --- a/litellm/integrations/arize_ai.py +++ b/litellm/integrations/arize_ai.py @@ -7,135 +7,208 @@ this file has Arize ai specific helper functions import json from typing import TYPE_CHECKING, Any, Optional, Union -from litellm._logging import verbose_proxy_logger +from litellm._logging import verbose_logger if TYPE_CHECKING: from opentelemetry.trace import Span as _Span + from .opentelemetry import OpenTelemetryConfig as _OpenTelemetryConfig + Span = _Span + OpenTelemetryConfig = _OpenTelemetryConfig else: Span = Any + OpenTelemetryConfig = Any + +import os + +from litellm.types.integrations.arize import * -def make_json_serializable(payload: dict) -> dict: - for key, value in payload.items(): +class ArizeLogger: + @staticmethod + def set_arize_ai_attributes(span: Span, kwargs, response_obj): + from litellm.integrations._types.open_inference import ( + MessageAttributes, + MessageContentAttributes, + OpenInferenceSpanKindValues, + SpanAttributes, + ) + try: - if isinstance(value, dict): - # recursively sanitize dicts - payload[key] = make_json_serializable(value.copy()) - elif not isinstance(value, (str, int, float, bool, type(None))): - # everything else becomes a string - payload[key] = str(value) - except Exception: - # non blocking if it can't cast to a str + + optional_params = kwargs.get("optional_params", {}) + # litellm_params = kwargs.get("litellm_params", {}) or {} + + ############################################# + ############ LLM CALL METADATA ############## + ############################################# + # commented out for now - looks like Arize AI could not log this + # metadata = litellm_params.get("metadata", {}) or {} + # span.set_attribute(SpanAttributes.METADATA, str(metadata)) + + ############################################# + ########## LLM Request Attributes ########### + ############################################# + + # The name of the LLM a request is being made to + if kwargs.get("model"): + span.set_attribute(SpanAttributes.LLM_MODEL_NAME, kwargs.get("model")) + + span.set_attribute( + SpanAttributes.OPENINFERENCE_SPAN_KIND, + OpenInferenceSpanKindValues.LLM.value, + ) + messages = kwargs.get("messages") + + # for /chat/completions + # https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions + if messages: + span.set_attribute( + SpanAttributes.INPUT_VALUE, + messages[-1].get("content", ""), # get the last message for input + ) + + # LLM_INPUT_MESSAGES shows up under `input_messages` tab on the span page + for idx, msg in enumerate(messages): + # Set the role per message + span.set_attribute( + f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_ROLE}", + msg["role"], + ) + # Set the content per message + span.set_attribute( + f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_CONTENT}", + msg.get("content", ""), + ) + + # The Generative AI Provider: Azure, OpenAI, etc. + _optional_params = ArizeLogger.make_json_serializable(optional_params) + _json_optional_params = json.dumps(_optional_params) + span.set_attribute( + SpanAttributes.LLM_INVOCATION_PARAMETERS, _json_optional_params + ) + + if optional_params.get("user"): + span.set_attribute(SpanAttributes.USER_ID, optional_params.get("user")) + + ############################################# + ########## LLM Response Attributes ########## + # https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions + ############################################# + for choice in response_obj.get("choices"): + response_message = choice.get("message", {}) + span.set_attribute( + SpanAttributes.OUTPUT_VALUE, response_message.get("content", "") + ) + + # This shows up under `output_messages` tab on the span page + # This code assumes a single response + span.set_attribute( + f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_ROLE}", + response_message["role"], + ) + span.set_attribute( + f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_CONTENT}", + response_message.get("content", ""), + ) + + usage = response_obj.get("usage") + if usage: + span.set_attribute( + SpanAttributes.LLM_TOKEN_COUNT_TOTAL, + usage.get("total_tokens"), + ) + + # The number of tokens used in the LLM response (completion). + span.set_attribute( + SpanAttributes.LLM_TOKEN_COUNT_COMPLETION, + usage.get("completion_tokens"), + ) + + # The number of tokens used in the LLM prompt. + span.set_attribute( + SpanAttributes.LLM_TOKEN_COUNT_PROMPT, + usage.get("prompt_tokens"), + ) pass - return payload + except Exception as e: + verbose_logger.error(f"Error setting arize attributes: {e}") + ###################### Helper functions ###################### -def set_arize_ai_attributes(span: Span, kwargs, response_obj): - from litellm.integrations._types.open_inference import ( - MessageAttributes, - MessageContentAttributes, - OpenInferenceSpanKindValues, - SpanAttributes, - ) + @staticmethod + def _get_arize_config() -> ArizeConfig: + """ + Helper function to get Arize configuration. - try: + Returns: + ArizeConfig: A Pydantic model containing Arize configuration. - optional_params = kwargs.get("optional_params", {}) - # litellm_params = kwargs.get("litellm_params", {}) or {} + Raises: + ValueError: If required environment variables are not set. + """ + space_key = os.environ.get("ARIZE_SPACE_KEY") + api_key = os.environ.get("ARIZE_API_KEY") - ############################################# - ############ LLM CALL METADATA ############## - ############################################# - # commented out for now - looks like Arize AI could not log this - # metadata = litellm_params.get("metadata", {}) or {} - # span.set_attribute(SpanAttributes.METADATA, str(metadata)) + if not space_key: + raise ValueError("ARIZE_SPACE_KEY not found in environment variables") + if not api_key: + raise ValueError("ARIZE_API_KEY not found in environment variables") - ############################################# - ########## LLM Request Attributes ########### - ############################################# - - # The name of the LLM a request is being made to - if kwargs.get("model"): - span.set_attribute(SpanAttributes.LLM_MODEL_NAME, kwargs.get("model")) - - span.set_attribute( - SpanAttributes.OPENINFERENCE_SPAN_KIND, - OpenInferenceSpanKindValues.LLM.value, - ) - messages = kwargs.get("messages") - - # for /chat/completions - # https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions - if messages: - span.set_attribute( - SpanAttributes.INPUT_VALUE, - messages[-1].get("content", ""), # get the last message for input + grpc_endpoint = os.environ.get("ARIZE_ENDPOINT") + http_endpoint = os.environ.get("ARIZE_HTTP_ENDPOINT") + if grpc_endpoint is None and http_endpoint is None: + # use default arize grpc endpoint + verbose_logger.debug( + "No ARIZE_ENDPOINT or ARIZE_HTTP_ENDPOINT found, using default endpoint: https://otlp.arize.com/v1" ) + grpc_endpoint = "https://otlp.arize.com/v1" - # LLM_INPUT_MESSAGES shows up under `input_messages` tab on the span page - for idx, msg in enumerate(messages): - # Set the role per message - span.set_attribute( - f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_ROLE}", - msg["role"], - ) - # Set the content per message - span.set_attribute( - f"{SpanAttributes.LLM_INPUT_MESSAGES}.{idx}.{MessageAttributes.MESSAGE_CONTENT}", - msg.get("content", ""), - ) - - # The Generative AI Provider: Azure, OpenAI, etc. - _optional_params = make_json_serializable(optional_params) - _json_optional_params = json.dumps(_optional_params) - span.set_attribute( - SpanAttributes.LLM_INVOCATION_PARAMETERS, _json_optional_params + return ArizeConfig( + space_key=space_key, + api_key=api_key, + grpc_endpoint=grpc_endpoint, + http_endpoint=http_endpoint, ) - if optional_params.get("user"): - span.set_attribute(SpanAttributes.USER_ID, optional_params.get("user")) + @staticmethod + def get_arize_opentelemetry_config() -> Optional[OpenTelemetryConfig]: + """ + Helper function to get OpenTelemetry configuration for Arize. - ############################################# - ########## LLM Response Attributes ########## - # https://docs.arize.com/arize/large-language-models/tracing/semantic-conventions - ############################################# - for choice in response_obj.get("choices"): - response_message = choice.get("message", {}) - span.set_attribute( - SpanAttributes.OUTPUT_VALUE, response_message.get("content", "") + Args: + arize_config (ArizeConfig): Arize configuration object. + + Returns: + OpenTelemetryConfig: Configuration for OpenTelemetry. + """ + from .opentelemetry import OpenTelemetryConfig + + arize_config = ArizeLogger._get_arize_config() + if arize_config.http_endpoint: + return OpenTelemetryConfig( + exporter="otlp_http", + endpoint=arize_config.http_endpoint, ) - # This shows up under `output_messages` tab on the span page - # This code assumes a single response - span.set_attribute( - f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_ROLE}", - response_message["role"], - ) - span.set_attribute( - f"{SpanAttributes.LLM_OUTPUT_MESSAGES}.0.{MessageAttributes.MESSAGE_CONTENT}", - response_message.get("content", ""), - ) + # use default arize grpc endpoint + return OpenTelemetryConfig( + exporter="otlp_grpc", + endpoint=arize_config.grpc_endpoint, + ) - usage = response_obj.get("usage") - if usage: - span.set_attribute( - SpanAttributes.LLM_TOKEN_COUNT_TOTAL, - usage.get("total_tokens"), - ) - - # The number of tokens used in the LLM response (completion). - span.set_attribute( - SpanAttributes.LLM_TOKEN_COUNT_COMPLETION, - usage.get("completion_tokens"), - ) - - # The number of tokens used in the LLM prompt. - span.set_attribute( - SpanAttributes.LLM_TOKEN_COUNT_PROMPT, - usage.get("prompt_tokens"), - ) - pass - except Exception as e: - verbose_proxy_logger.error(f"Error setting arize attributes: {e}") + @staticmethod + def make_json_serializable(payload: dict) -> dict: + for key, value in payload.items(): + try: + if isinstance(value, dict): + # recursively sanitize dicts + payload[key] = ArizeLogger.make_json_serializable(value.copy()) + elif not isinstance(value, (str, int, float, bool, type(None))): + # everything else becomes a string + payload[key] = str(value) + except Exception: + # non blocking if it can't cast to a str + pass + return payload diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 171ec21e77..8ba871acc8 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -396,9 +396,9 @@ class OpenTelemetry(CustomLogger): def set_attributes(self, span: Span, kwargs, response_obj): # noqa: PLR0915 try: if self.callback_name == "arize": - from litellm.integrations.arize_ai import set_arize_ai_attributes + from litellm.integrations.arize_ai import ArizeLogger - set_arize_ai_attributes(span, kwargs, response_obj) + ArizeLogger.set_arize_ai_attributes(span, kwargs, response_obj) return elif self.callback_name == "langtrace": from litellm.integrations.langtrace import LangtraceAttributes diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index f41ac256bb..7aee381516 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -60,6 +60,7 @@ from litellm.utils import ( from ..integrations.aispend import AISpendLogger from ..integrations.argilla import ArgillaLogger +from ..integrations.arize_ai import ArizeLogger from ..integrations.athina import AthinaLogger from ..integrations.braintrust_logging import BraintrustLogger from ..integrations.datadog.datadog import DataDogLogger @@ -2323,22 +2324,16 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 _in_memory_loggers.append(_opik_logger) return _opik_logger # type: ignore elif logging_integration == "arize": - if "ARIZE_SPACE_KEY" not in os.environ: - raise ValueError("ARIZE_SPACE_KEY not found in environment variables") - if "ARIZE_API_KEY" not in os.environ: - raise ValueError("ARIZE_API_KEY not found in environment variables") from litellm.integrations.opentelemetry import ( OpenTelemetry, OpenTelemetryConfig, ) - arize_endpoint = ( - os.environ.get("ARIZE_ENDPOINT", None) or "https://otlp.arize.com/v1" - ) - otel_config = OpenTelemetryConfig( - exporter="otlp_grpc", - endpoint=arize_endpoint, - ) + otel_config = ArizeLogger.get_arize_opentelemetry_config() + if otel_config is None: + raise ValueError( + "No valid endpoint found for Arize, please set 'ARIZE_ENDPOINT' to your GRPC endpoint or 'ARIZE_HTTP_ENDPOINT' to your HTTP endpoint" + ) os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( f"space_key={os.getenv('ARIZE_SPACE_KEY')},api_key={os.getenv('ARIZE_API_KEY')}" ) @@ -2351,7 +2346,6 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 _otel_logger = OpenTelemetry(config=otel_config, callback_name="arize") _in_memory_loggers.append(_otel_logger) return _otel_logger # type: ignore - elif logging_integration == "otel": from litellm.integrations.opentelemetry import OpenTelemetry diff --git a/litellm/types/integrations/arize.py b/litellm/types/integrations/arize.py new file mode 100644 index 0000000000..3c0bbcde0e --- /dev/null +++ b/litellm/types/integrations/arize.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import BaseModel + + +class ArizeConfig(BaseModel): + space_key: str + api_key: str + grpc_endpoint: Optional[str] = None + http_endpoint: Optional[str] = None diff --git a/tests/local_testing/test_arize_ai.py b/tests/local_testing/test_arize_ai.py index f6ccc75f25..24aed3da7a 100644 --- a/tests/local_testing/test_arize_ai.py +++ b/tests/local_testing/test_arize_ai.py @@ -10,9 +10,9 @@ from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanE import litellm from litellm._logging import verbose_logger, verbose_proxy_logger from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig +from litellm.integrations.arize_ai import ArizeConfig, ArizeLogger load_dotenv() -import logging @pytest.mark.asyncio() @@ -32,3 +32,57 @@ async def test_async_otel_callback(): ) await asyncio.sleep(2) + + +@pytest.fixture +def mock_env_vars(monkeypatch): + monkeypatch.setenv("ARIZE_SPACE_KEY", "test_space_key") + monkeypatch.setenv("ARIZE_API_KEY", "test_api_key") + + +def test_get_arize_config(mock_env_vars): + """ + Use Arize default endpoint when no endpoints are provided + """ + config = ArizeLogger._get_arize_config() + assert isinstance(config, ArizeConfig) + assert config.space_key == "test_space_key" + assert config.api_key == "test_api_key" + assert config.grpc_endpoint == "https://otlp.arize.com/v1" + assert config.http_endpoint is None + + +def test_get_arize_config_with_endpoints(mock_env_vars, monkeypatch): + """ + Use provided endpoints when they are set + """ + monkeypatch.setenv("ARIZE_ENDPOINT", "grpc://test.endpoint") + monkeypatch.setenv("ARIZE_HTTP_ENDPOINT", "http://test.endpoint") + + config = ArizeLogger._get_arize_config() + assert config.grpc_endpoint == "grpc://test.endpoint" + assert config.http_endpoint == "http://test.endpoint" + + +def test_get_arize_opentelemetry_config_grpc(mock_env_vars, monkeypatch): + """ + Use provided GRPC endpoint when it is set + """ + monkeypatch.setenv("ARIZE_ENDPOINT", "grpc://test.endpoint") + + config = ArizeLogger.get_arize_opentelemetry_config() + assert isinstance(config, OpenTelemetryConfig) + assert config.exporter == "otlp_grpc" + assert config.endpoint == "grpc://test.endpoint" + + +def test_get_arize_opentelemetry_config_http(mock_env_vars, monkeypatch): + """ + Use provided HTTP endpoint when it is set + """ + monkeypatch.setenv("ARIZE_HTTP_ENDPOINT", "http://test.endpoint") + + config = ArizeLogger.get_arize_opentelemetry_config() + assert isinstance(config, OpenTelemetryConfig) + assert config.exporter == "otlp_http" + assert config.endpoint == "http://test.endpoint" From cb2563e3c0e49e2c9187ccfc88fd05fbbbfe6303 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Tue, 22 Oct 2024 21:18:54 -0700 Subject: [PATCH 12/62] Litellm dev 10 22 2024 (#6384) * fix(utils.py): add 'disallowed_special' for token counting on .encode() Fixes error when '< endoftext >' in string * Revert "(fix) standard logging metadata + add unit testing (#6366)" (#6381) This reverts commit 8359cb6fa9bf7b0bf4f3df630cf8666adffa2813. * add new 35 mode lcard (#6378) * Add claude 3 5 sonnet 20241022 models for all provides (#6380) * Add Claude 3.5 v2 on Amazon Bedrock and Vertex AI. * added anthropic/claude-3-5-sonnet-20241022 * add new 35 mode lcard --------- Co-authored-by: Paul Gauthier Co-authored-by: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> * test(skip-flaky-google-context-caching-test): google is not reliable. their sample code is also not working * Fix metadata being overwritten in speech() (#6295) * fix: adding missing redis cluster kwargs (#6318) Co-authored-by: Ali Arian * Add support for `max_completion_tokens` in Azure OpenAI (#6376) Now that Azure supports `max_completion_tokens`, no need for special handling for this param and let it pass thru. More details: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#api-support * build(model_prices_and_context_window.json): add voyage-finance-2 pricing Closes https://github.com/BerriAI/litellm/issues/6371 * build(model_prices_and_context_window.json): fix llama3.1 pricing model name on map Closes https://github.com/BerriAI/litellm/issues/6310 * feat(realtime_streaming.py): just log specific events Closes https://github.com/BerriAI/litellm/issues/6267 * fix(utils.py): more robust checking if unmapped vertex anthropic model belongs to that family of models Fixes https://github.com/BerriAI/litellm/issues/6383 * Fix Ollama stream handling for tool calls with None content (#6155) * test(test_max_completions): update test now that azure supports 'max_completion_tokens' * fix(handler.py): fix linting error --------- Co-authored-by: Ishaan Jaff Co-authored-by: Low Jian Sheng <15527690+lowjiansheng@users.noreply.github.com> Co-authored-by: David Manouchehri Co-authored-by: Paul Gauthier Co-authored-by: John HU Co-authored-by: Ali Arian <113945203+ali-arian@users.noreply.github.com> Co-authored-by: Ali Arian Co-authored-by: Anand Taralika <46954145+taralika@users.noreply.github.com> Co-authored-by: Nolan Tremelling <34580718+NolanTrem@users.noreply.github.com> --- docs/my-website/docs/realtime.md | 19 +++++++- litellm/__init__.py | 1 + litellm/_redis.py | 2 + .../litellm_core_utils/realtime_streaming.py | 29 +++++++++++- .../AzureOpenAI/chat/gpt_transformation.py | 3 -- litellm/llms/AzureOpenAI/realtime/handler.py | 4 +- litellm/llms/ollama.py | 1 + .../anthropic/transformation.py | 13 +++++ litellm/main.py | 1 - ...odel_prices_and_context_window_backup.json | 14 ++++-- litellm/proxy/_new_secret_config.yaml | 12 +++-- litellm/utils.py | 10 ++-- model_prices_and_context_window.json | 14 ++++-- .../test_max_completion_tokens.py | 2 +- tests/llm_translation/test_optional_params.py | 9 ++++ tests/local_testing/test_completion_cost.py | 47 +++++++++++++++++++ tests/local_testing/test_token_counter.py | 4 ++ 17 files changed, 162 insertions(+), 23 deletions(-) diff --git a/docs/my-website/docs/realtime.md b/docs/my-website/docs/realtime.md index 2149387a66..28697f44b9 100644 --- a/docs/my-website/docs/realtime.md +++ b/docs/my-website/docs/realtime.md @@ -83,4 +83,21 @@ ws.on("message", function incoming(message) { ws.on("error", function handleError(error) { console.error("Error: ", error); }); -``` \ No newline at end of file +``` + +## Logging + +To prevent requests from being dropped, by default LiteLLM just logs these event types: + +- `session.created` +- `response.create` +- `response.done` + +You can override this by setting the `logged_real_time_event_types` parameter in the config. For example: + +```yaml +litellm_settings: + logged_real_time_event_types: "*" # Log all events + ## OR ## + logged_real_time_event_types: ["session.created", "response.create", "response.done"] # Log only these event types +``` diff --git a/litellm/__init__.py b/litellm/__init__.py index 357057e4cc..2aa89a03c8 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -56,6 +56,7 @@ _custom_logger_compatible_callbacks_literal = Literal[ "opik", "argilla", ] +logged_real_time_event_types: Optional[Union[List[str], Literal["*"]]] = None _known_custom_logger_compatible_callbacks: List = list( get_args(_custom_logger_compatible_callbacks_literal) ) diff --git a/litellm/_redis.py b/litellm/_redis.py index 289a7d4aed..c058a0d3a8 100644 --- a/litellm/_redis.py +++ b/litellm/_redis.py @@ -69,6 +69,8 @@ def _get_redis_cluster_kwargs(client=None): available_args = [x for x in arg_spec.args if x not in exclude_args] available_args.append("password") + available_args.append("username") + available_args.append("ssl") return available_args diff --git a/litellm/litellm_core_utils/realtime_streaming.py b/litellm/litellm_core_utils/realtime_streaming.py index 922f90e360..440deac1cc 100644 --- a/litellm/litellm_core_utils/realtime_streaming.py +++ b/litellm/litellm_core_utils/realtime_streaming.py @@ -26,15 +26,24 @@ async with websockets.connect( # type: ignore import asyncio import concurrent.futures +import json import traceback from asyncio import Task from typing import Any, Dict, List, Optional, Union +import litellm + from .litellm_logging import Logging as LiteLLMLogging # Create a thread pool with a maximum of 10 threads executor = concurrent.futures.ThreadPoolExecutor(max_workers=10) +DefaultLoggedRealTimeEventTypes = [ + "session.created", + "response.create", + "response.done", +] + class RealTimeStreaming: def __init__( @@ -49,9 +58,27 @@ class RealTimeStreaming: self.messages: List = [] self.input_message: Dict = {} + _logged_real_time_event_types = litellm.logged_real_time_event_types + + if _logged_real_time_event_types is None: + _logged_real_time_event_types = DefaultLoggedRealTimeEventTypes + self.logged_real_time_event_types = _logged_real_time_event_types + + def _should_store_message(self, message: Union[str, bytes]) -> bool: + if isinstance(message, bytes): + message = message.decode("utf-8") + message_obj = json.loads(message) + _msg_type = message_obj["type"] + if self.logged_real_time_event_types == "*": + return True + if _msg_type in self.logged_real_time_event_types: + return True + return False + def store_message(self, message: Union[str, bytes]): """Store message in list""" - self.messages.append(message) + if self._should_store_message(message): + self.messages.append(message) def store_input(self, message: dict): """Store input message""" diff --git a/litellm/llms/AzureOpenAI/chat/gpt_transformation.py b/litellm/llms/AzureOpenAI/chat/gpt_transformation.py index 271c5c467a..fb0b21d3b3 100644 --- a/litellm/llms/AzureOpenAI/chat/gpt_transformation.py +++ b/litellm/llms/AzureOpenAI/chat/gpt_transformation.py @@ -198,9 +198,6 @@ class AzureOpenAIConfig: optional_params["json_mode"] = True else: optional_params["response_format"] = value - elif param == "max_completion_tokens": - # TODO - Azure OpenAI will probably add support for this, we should pass it through when Azure adds support - optional_params["max_tokens"] = value elif param in supported_openai_params: optional_params[param] = value diff --git a/litellm/llms/AzureOpenAI/realtime/handler.py b/litellm/llms/AzureOpenAI/realtime/handler.py index bf45c53fbd..a6c0f1967b 100644 --- a/litellm/llms/AzureOpenAI/realtime/handler.py +++ b/litellm/llms/AzureOpenAI/realtime/handler.py @@ -72,5 +72,5 @@ class AzureOpenAIRealtime(AzureChatCompletion): except websockets.exceptions.InvalidStatusCode as e: # type: ignore await websocket.close(code=e.status_code, reason=str(e)) - except Exception as e: - await websocket.close(code=1011, reason=f"Internal server error: {str(e)}") + except Exception: + pass diff --git a/litellm/llms/ollama.py b/litellm/llms/ollama.py index e08e6f6938..845d0e2ddd 100644 --- a/litellm/llms/ollama.py +++ b/litellm/llms/ollama.py @@ -398,6 +398,7 @@ def ollama_completion_stream(url, data, logging_obj): isinstance(content_chunk, StreamingChoices) and hasattr(content_chunk, "delta") and hasattr(content_chunk.delta, "content") + and content_chunk.delta.content is not None ): content_chunks.append(content_chunk.delta.content) response_content = "".join(content_chunks) diff --git a/litellm/llms/vertex_ai_and_google_ai_studio/vertex_ai_partner_models/anthropic/transformation.py b/litellm/llms/vertex_ai_and_google_ai_studio/vertex_ai_partner_models/anthropic/transformation.py index 44b8af2795..406314a593 100644 --- a/litellm/llms/vertex_ai_and_google_ai_studio/vertex_ai_partner_models/anthropic/transformation.py +++ b/litellm/llms/vertex_ai_and_google_ai_studio/vertex_ai_partner_models/anthropic/transformation.py @@ -177,3 +177,16 @@ class VertexAIAnthropicConfig: optional_params["json_mode"] = True return optional_params + + @classmethod + def is_supported_model( + cls, model: str, custom_llm_provider: Optional[str] = None + ) -> bool: + """ + Check if the model is supported by the VertexAI Anthropic API. + """ + if custom_llm_provider == "vertex_ai" and "claude" in model.lower(): + return True + elif model in litellm.vertex_anthropic_models: + return True + return False diff --git a/litellm/main.py b/litellm/main.py index 0e62c9fa2f..f239d2612c 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -4986,7 +4986,6 @@ def speech( litellm_call_id: Optional[str] = kwargs.get("litellm_call_id", None) proxy_server_request = kwargs.get("proxy_server_request", None) model_info = kwargs.get("model_info", None) - metadata = kwargs.get("metadata", {}) model, custom_llm_provider, dynamic_api_key, api_base = get_llm_provider(model=model, custom_llm_provider=custom_llm_provider, api_base=api_base) # type: ignore kwargs.pop("tags", []) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index e637418049..890ef86883 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -1104,7 +1104,7 @@ "litellm_provider": "azure_ai", "mode": "chat" }, - "azure_ai/Meta-Llama-31-8B-Instruct": { + "azure_ai/Meta-Llama-3.1-8B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -1114,7 +1114,7 @@ "mode": "chat", "source":"https://azuremarketplace.microsoft.com/en-us/marketplace/apps/metagenai.meta-llama-3-1-8b-instruct-offer?tab=PlansAndPrice" }, - "azure_ai/Meta-Llama-31-70B-Instruct": { + "azure_ai/Meta-Llama-3.1-70B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -1124,7 +1124,7 @@ "mode": "chat", "source":"https://azuremarketplace.microsoft.com/en-us/marketplace/apps/metagenai.meta-llama-3-1-70b-instruct-offer?tab=PlansAndPrice" }, - "azure_ai/Meta-Llama-31-405B-Instruct": { + "azure_ai/Meta-Llama-3.1-405B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -6446,6 +6446,14 @@ "litellm_provider": "voyage", "mode": "embedding" }, + "voyage/voyage-finance-2": { + "max_tokens": 4000, + "max_input_tokens": 4000, + "input_cost_per_token": 0.00000012, + "output_cost_per_token": 0.000000, + "litellm_provider": "voyage", + "mode": "embedding" + }, "databricks/databricks-meta-llama-3-1-405b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 105fabbdda..00f4da8d91 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -1,8 +1,10 @@ model_list: - - model_name: gpt-3.5-turbo - litellm_params: - model: gpt-3.5-turbo - api_key: os.environ/OPENAI_API_KEY + - model_name: gpt-4o + litellm_params: + model: azure/gpt-4o-realtime-preview + api_key: os.environ/AZURE_SWEDEN_API_KEY + api_base: os.environ/AZURE_SWEDEN_API_BASE litellm_settings: - callbacks: ["prometheus"] \ No newline at end of file + success_callback: ["langfuse"] + # logged_real_time_event_types: "*" \ No newline at end of file diff --git a/litellm/utils.py b/litellm/utils.py index e82a937bb4..6b3f0a80ca 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -126,6 +126,7 @@ except (ImportError, AttributeError): os.environ["TIKTOKEN_CACHE_DIR"] = os.getenv( "CUSTOM_TIKTOKEN_CACHE_DIR", filename ) # use local copy of tiktoken b/c of - https://github.com/BerriAI/litellm/issues/1071 +from tiktoken import Encoding encoding = tiktoken.get_encoding("cl100k_base") from importlib import resources @@ -1278,7 +1279,10 @@ def encode(model="", text="", custom_tokenizer: Optional[dict] = None): enc: The encoded text. """ tokenizer_json = custom_tokenizer or _select_tokenizer(model=model) - enc = tokenizer_json["tokenizer"].encode(text) + if isinstance(tokenizer_json["tokenizer"], Encoding): + enc = tokenizer_json["tokenizer"].encode(text, disallowed_special=()) + else: + enc = tokenizer_json["tokenizer"].encode(text) return enc @@ -3045,8 +3049,8 @@ def get_optional_params( # noqa: PLR0915 ) if litellm.vertex_ai_safety_settings is not None: optional_params["safety_settings"] = litellm.vertex_ai_safety_settings - elif ( - custom_llm_provider == "vertex_ai" and model in litellm.vertex_anthropic_models + elif litellm.VertexAIAnthropicConfig.is_supported_model( + model=model, custom_llm_provider=custom_llm_provider ): supported_params = get_supported_openai_params( model=model, custom_llm_provider=custom_llm_provider diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index e637418049..890ef86883 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -1104,7 +1104,7 @@ "litellm_provider": "azure_ai", "mode": "chat" }, - "azure_ai/Meta-Llama-31-8B-Instruct": { + "azure_ai/Meta-Llama-3.1-8B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -1114,7 +1114,7 @@ "mode": "chat", "source":"https://azuremarketplace.microsoft.com/en-us/marketplace/apps/metagenai.meta-llama-3-1-8b-instruct-offer?tab=PlansAndPrice" }, - "azure_ai/Meta-Llama-31-70B-Instruct": { + "azure_ai/Meta-Llama-3.1-70B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -1124,7 +1124,7 @@ "mode": "chat", "source":"https://azuremarketplace.microsoft.com/en-us/marketplace/apps/metagenai.meta-llama-3-1-70b-instruct-offer?tab=PlansAndPrice" }, - "azure_ai/Meta-Llama-31-405B-Instruct": { + "azure_ai/Meta-Llama-3.1-405B-Instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, @@ -6446,6 +6446,14 @@ "litellm_provider": "voyage", "mode": "embedding" }, + "voyage/voyage-finance-2": { + "max_tokens": 4000, + "max_input_tokens": 4000, + "input_cost_per_token": 0.00000012, + "output_cost_per_token": 0.000000, + "litellm_provider": "voyage", + "mode": "embedding" + }, "databricks/databricks-meta-llama-3-1-405b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, diff --git a/tests/llm_translation/test_max_completion_tokens.py b/tests/llm_translation/test_max_completion_tokens.py index d0819e544d..de335a3c55 100644 --- a/tests/llm_translation/test_max_completion_tokens.py +++ b/tests/llm_translation/test_max_completion_tokens.py @@ -235,7 +235,7 @@ def test_all_model_configs(): optional_params={}, api_version="2022-12-01", drop_params=False, - ) == {"max_tokens": 10} + ) == {"max_completion_tokens": 10} from litellm.llms.bedrock.chat.converse_transformation import AmazonConverseConfig diff --git a/tests/llm_translation/test_optional_params.py b/tests/llm_translation/test_optional_params.py index 728ec1507f..a0387ce1b2 100644 --- a/tests/llm_translation/test_optional_params.py +++ b/tests/llm_translation/test_optional_params.py @@ -775,3 +775,12 @@ def test_hosted_vllm_tool_param(): ) assert "tools" not in optional_params assert "tool_choice" not in optional_params + + +def test_unmapped_vertex_anthropic_model(): + optional_params = get_optional_params( + model="claude-3-5-sonnet-v250@20241022", + custom_llm_provider="vertex_ai", + max_retries=10, + ) + assert "max_retries" not in optional_params diff --git a/tests/local_testing/test_completion_cost.py b/tests/local_testing/test_completion_cost.py index fcd80650a6..21659a4c1b 100644 --- a/tests/local_testing/test_completion_cost.py +++ b/tests/local_testing/test_completion_cost.py @@ -2587,3 +2587,50 @@ async def test_test_completion_cost_gpt4o_audio_output_from_model(stream): total_output_cost = output_audio_cost + output_text_cost assert round(cost, 2) == round(total_input_cost + total_output_cost, 2) + + +def test_completion_cost_azure_ai_meta(): + """ + Relevant issue: https://github.com/BerriAI/litellm/issues/6310 + """ + from litellm import ModelResponse + + os.environ["LITELLM_LOCAL_MODEL_COST_MAP"] = "True" + litellm.model_cost = litellm.get_model_cost_map(url="") + + litellm.set_verbose = True + response = { + "id": "cmpl-55db75e0b05344058b0bd8ee4e00bf84", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "logprobs": None, + "message": { + "content": 'Here\'s one:\n\nWhy did the Linux kernel go to therapy?\n\nBecause it had a lot of "core" issues!\n\nHope that one made you laugh!', + "refusal": None, + "role": "assistant", + "audio": None, + "function_call": None, + "tool_calls": [], + }, + } + ], + "created": 1729243714, + "model": "azure_ai/Meta-Llama-3.1-70B-Instruct", + "object": "chat.completion", + "service_tier": None, + "system_fingerprint": None, + "usage": { + "completion_tokens": 32, + "prompt_tokens": 16, + "total_tokens": 48, + "completion_tokens_details": None, + "prompt_tokens_details": None, + }, + } + + model_response = ModelResponse(**response) + cost = completion_cost(model_response, custom_llm_provider="azure_ai") + + assert cost > 0 diff --git a/tests/local_testing/test_token_counter.py b/tests/local_testing/test_token_counter.py index 6dbf286e4e..3ad73f2d8b 100644 --- a/tests/local_testing/test_token_counter.py +++ b/tests/local_testing/test_token_counter.py @@ -375,3 +375,7 @@ def test_img_url_token_counter(img_url): assert width is not None assert height is not None + + +def test_token_encode_disallowed_special(): + encode(model="gpt-3.5-turbo", text="Hello, world! <|endoftext|>") From 0a92923f86bd6361bccae0447f5a4566a580a47a Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Tue, 22 Oct 2024 22:39:39 -0700 Subject: [PATCH 13/62] =?UTF-8?q?bump:=20version=201.50.2=20=E2=86=92=201.?= =?UTF-8?q?50.3?= 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 a8caf4769e..9122eac93f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.50.2" +version = "1.50.3" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -91,7 +91,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.50.2" +version = "1.50.3" version_files = [ "pyproject.toml:^version" ] From 64c3d3210c85b7e7a6fede95000f31ae47f8387f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Oct 2024 13:30:12 +0530 Subject: [PATCH 14/62] build(deps): bump http-proxy-middleware in /docs/my-website (#6395) Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/my-website/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/my-website/package-lock.json b/docs/my-website/package-lock.json index da0133c1cd..27bf4744aa 100644 --- a/docs/my-website/package-lock.json +++ b/docs/my-website/package-lock.json @@ -12447,9 +12447,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", From 807e9dcea8c71fd6f0a791e152f43b3d1cd4de1b Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 14:09:35 +0530 Subject: [PATCH 15/62] (docs + testing) Correctly document the timeout value used by litellm proxy is 6000 seconds + add to best practices for prod (#6339) * fix docs use documented timeout * document request timeout * add test for litellm.request_timeout * add test for checking value of timeout --- docs/my-website/docs/proxy/cli.md | 2 +- docs/my-website/docs/proxy/configs.md | 2 ++ docs/my-website/docs/proxy/prod.md | 1 + docs/my-website/docs/simple_proxy_old_doc.md | 2 +- .../health_endpoints/_health_endpoints.py | 29 ++++++++++++++++++- litellm/proxy/proxy_cli.py | 2 +- tests/local_testing/test_proxy_server.py | 20 +++++++++++++ 7 files changed, 54 insertions(+), 4 deletions(-) diff --git a/docs/my-website/docs/proxy/cli.md b/docs/my-website/docs/proxy/cli.md index 5ce7a05eca..d0c477a4ee 100644 --- a/docs/my-website/docs/proxy/cli.md +++ b/docs/my-website/docs/proxy/cli.md @@ -135,7 +135,7 @@ Cli arguments, --host, --port, --num_workers ``` ## --request_timeout - - **Default:** `600` + - **Default:** `6000` - **Type:** `int` - Set the timeout in seconds for completion calls. - **Usage:** diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index aa79242d4b..bf16a96e66 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -625,6 +625,7 @@ litellm_settings: redact_user_api_key_info: boolean # Redact information about the user api key (hashed token, user_id, team id, etc.), from logs. Currently supported for Langfuse, OpenTelemetry, Logfire, ArizeAI logging. langfuse_default_tags: ["cache_hit", "cache_key", "proxy_base_url", "user_api_key_alias", "user_api_key_user_id", "user_api_key_user_email", "user_api_key_team_alias", "semantic-similarity", "proxy_base_url"] # default tags for Langfuse Logging + request_timeout: 10 # (int) llm requesttimeout in seconds. Raise Timeout error if call takes longer than 10s. Sets litellm.request_timeout set_verbose: boolean # sets litellm.set_verbose=True to view verbose debug logs. DO NOT LEAVE THIS ON IN PRODUCTION json_logs: boolean # if true, logs will be in json format @@ -721,6 +722,7 @@ general_settings: | set_verbose | boolean | If true, sets litellm.set_verbose=True to view verbose debug logs. DO NOT LEAVE THIS ON IN PRODUCTION | | json_logs | boolean | If true, logs will be in json format. If you need to store the logs as JSON, just set the `litellm.json_logs = True`. We currently just log the raw POST request from litellm as a JSON [Further docs](./debugging) | | default_fallbacks | array of strings | List of fallback models to use if a specific model group is misconfigured / bad. [Further docs](./reliability#default-fallbacks) | +| request_timeout | integer | The timeout for requests in seconds. If not set, the default value is `6000 seconds`. [For reference OpenAI Python SDK defaults to `600 seconds`.](https://github.com/openai/openai-python/blob/main/src/openai/_constants.py) | | content_policy_fallbacks | array of objects | Fallbacks to use when a ContentPolicyViolationError is encountered. [Further docs](./reliability#content-policy-fallbacks) | | context_window_fallbacks | array of objects | Fallbacks to use when a ContextWindowExceededError is encountered. [Further docs](./reliability#context-window-fallbacks) | | cache | boolean | If true, enables caching. [Further docs](./caching) | diff --git a/docs/my-website/docs/proxy/prod.md b/docs/my-website/docs/proxy/prod.md index c42b07d8e7..99fa19e772 100644 --- a/docs/my-website/docs/proxy/prod.md +++ b/docs/my-website/docs/proxy/prod.md @@ -21,6 +21,7 @@ general_settings: database_connection_pool_limit: 10 # limit the number of database connections to = MAX Number of DB Connections/Number of instances of litellm proxy (Around 10-20 is good number) litellm_settings: + request_timeout: 600 # raise Timeout error if call takes longer than 600 seconds. Default value is 6000seconds if not set set_verbose: False # Switch off Debug Logging, ensure your logs do not have any debugging on json_logs: true # Get debug logs in json format ``` diff --git a/docs/my-website/docs/simple_proxy_old_doc.md b/docs/my-website/docs/simple_proxy_old_doc.md index 2d68db3296..64491b1ea8 100644 --- a/docs/my-website/docs/simple_proxy_old_doc.md +++ b/docs/my-website/docs/simple_proxy_old_doc.md @@ -1312,7 +1312,7 @@ LiteLLM proxy adds **0.00325 seconds** latency as compared to using the Raw Open ``` #### --request_timeout - - **Default:** `600` + - **Default:** `6000` - **Type:** `int` - Set the timeout in seconds for completion calls. - **Usage:** diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 78b2a3d205..1f2eb5d6d6 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -372,6 +372,11 @@ async def _db_health_readiness_check(): return db_health_cache +@router.get( + "/settings", + tags=["health"], + dependencies=[Depends(user_api_key_auth)], +) @router.get( "/active/callbacks", tags=["health"], @@ -379,8 +384,29 @@ async def _db_health_readiness_check(): ) async def active_callbacks(): """ - Returns a list of active callbacks on litellm.callbacks, litellm.input_callback, litellm.failure_callback, litellm.success_callback + Returns a list of litellm level settings + + This is useful for debugging and ensuring the proxy server is configured correctly. + + Response schema: + ``` + { + "alerting": _alerting, + "litellm.callbacks": litellm_callbacks, + "litellm.input_callback": litellm_input_callbacks, + "litellm.failure_callback": litellm_failure_callbacks, + "litellm.success_callback": litellm_success_callbacks, + "litellm._async_success_callback": litellm_async_success_callbacks, + "litellm._async_failure_callback": litellm_async_failure_callbacks, + "litellm._async_input_callback": litellm_async_input_callbacks, + "all_litellm_callbacks": all_litellm_callbacks, + "num_callbacks": len(all_litellm_callbacks), + "num_alerting": _num_alerting, + "litellm.request_timeout": litellm.request_timeout, + } + ``` """ + from litellm.proxy.proxy_server import general_settings, proxy_logging_obj _alerting = str(general_settings.get("alerting")) @@ -421,6 +447,7 @@ async def active_callbacks(): "all_litellm_callbacks": all_litellm_callbacks, "num_callbacks": len(all_litellm_callbacks), "num_alerting": _num_alerting, + "litellm.request_timeout": litellm.request_timeout, } diff --git a/litellm/proxy/proxy_cli.py b/litellm/proxy/proxy_cli.py index d683bfe097..1fb628a809 100644 --- a/litellm/proxy/proxy_cli.py +++ b/litellm/proxy/proxy_cli.py @@ -125,7 +125,7 @@ def is_port_in_use(port): ) @click.option( "--request_timeout", - default=600, + default=6000, type=int, help="Set timeout in seconds for completion calls", ) diff --git a/tests/local_testing/test_proxy_server.py b/tests/local_testing/test_proxy_server.py index 3dbe417ea7..d76894ce69 100644 --- a/tests/local_testing/test_proxy_server.py +++ b/tests/local_testing/test_proxy_server.py @@ -173,6 +173,26 @@ def test_chat_completion(mock_acompletion, client_no_auth): pytest.fail(f"LiteLLM Proxy test failed. Exception - {str(e)}") +def test_get_settings_request_timeout(client_no_auth): + """ + When no timeout is set, it should use the litellm.request_timeout value + """ + # Set a known value for litellm.request_timeout + import litellm + + # Make a GET request to /settings + response = client_no_auth.get("/settings") + + # Check if the request was successful + assert response.status_code == 200 + + # Parse the JSON response + settings = response.json() + print("settings", settings) + + assert settings["litellm.request_timeout"] == litellm.request_timeout + + @pytest.mark.parametrize( "litellm_key_header_name", ["x-litellm-key", None], From ae01de067daeba9d080ef328744917e61496991e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 15:43:03 +0530 Subject: [PATCH 16/62] add code cov checks --- codecov.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/codecov.yaml b/codecov.yaml index a76f035522..f949ad7ca0 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -18,4 +18,15 @@ component_management: paths: - "*/proxy/auth/**" comment: - layout: "header, diff, flags, components" # show component info in the PR comment \ No newline at end of file + layout: "header, diff, flags, components" # show component info in the PR comment + +coverage: + status: + project: + default: + target: auto + threshold: 1 # at maximum allow coverage to drop by 1% + patch: + default: + target: auto + threshold: 80% # at minimum patch coverage should be 80% \ No newline at end of file From 85f1e5ccfdba71cd75ce758e85268668617da76f Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 15:44:27 +0530 Subject: [PATCH 17/62] fix comment --- codecov.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codecov.yaml b/codecov.yaml index f949ad7ca0..c25cf0fbae 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -25,8 +25,8 @@ coverage: project: default: target: auto - threshold: 1 # at maximum allow coverage to drop by 1% + threshold: 1% # at maximum allow project coverage to drop by 1% patch: default: target: auto - threshold: 80% # at minimum patch coverage should be 80% \ No newline at end of file + threshold: 0% # patch coverage should be 100% From 3991d75511b81161e9d1a1c69e58def31263d107 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 17:32:14 +0530 Subject: [PATCH 18/62] (refactor) move convert dict to model response to llm_response_utils/ (#6393) * refactor move convert dict to model response * fix imports * fix import _handle_invalid_parallel_tool_calls --- .../convert_dict_to_response.py | 493 ++++++++++++++++++ litellm/utils.py | 473 +---------------- 2 files changed, 499 insertions(+), 467 deletions(-) create mode 100644 litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py diff --git a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py new file mode 100644 index 0000000000..fe69d837c9 --- /dev/null +++ b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py @@ -0,0 +1,493 @@ +import asyncio +import json +import time +import traceback +import uuid +from typing import Dict, Iterable, List, Literal, Optional, Union + +import litellm +from litellm._logging import verbose_logger +from litellm.types.utils import ( + ChatCompletionDeltaToolCall, + ChatCompletionMessageToolCall, + Choices, + Delta, + EmbeddingResponse, + Function, + ImageResponse, + Message, + ModelResponse, + RerankResponse, + StreamingChoices, + TranscriptionResponse, + Usage, +) + +from .get_headers import get_response_headers + + +async def convert_to_streaming_response_async(response_object: Optional[dict] = None): + """ + Asynchronously converts a response object to a streaming response. + + Args: + response_object (Optional[dict]): The response object to be converted. Defaults to None. + + Raises: + Exception: If the response object is None. + + Yields: + ModelResponse: The converted streaming response object. + + Returns: + None + """ + if response_object is None: + raise Exception("Error in response object format") + + model_response_object = ModelResponse(stream=True) + + if model_response_object is None: + raise Exception("Error in response creating model response object") + + choice_list = [] + + for idx, choice in enumerate(response_object["choices"]): + if ( + choice["message"].get("tool_calls", None) is not None + and isinstance(choice["message"]["tool_calls"], list) + and len(choice["message"]["tool_calls"]) > 0 + and isinstance(choice["message"]["tool_calls"][0], dict) + ): + pydantic_tool_calls = [] + for index, t in enumerate(choice["message"]["tool_calls"]): + if "index" not in t: + t["index"] = index + pydantic_tool_calls.append(ChatCompletionDeltaToolCall(**t)) + choice["message"]["tool_calls"] = pydantic_tool_calls + delta = Delta( + content=choice["message"].get("content", None), + role=choice["message"]["role"], + function_call=choice["message"].get("function_call", None), + tool_calls=choice["message"].get("tool_calls", None), + ) + finish_reason = choice.get("finish_reason", None) + + if finish_reason is None: + finish_reason = choice.get("finish_details") + + logprobs = choice.get("logprobs", None) + + choice = StreamingChoices( + finish_reason=finish_reason, index=idx, delta=delta, logprobs=logprobs + ) + choice_list.append(choice) + + model_response_object.choices = choice_list + + if "usage" in response_object and response_object["usage"] is not None: + setattr( + model_response_object, + "usage", + Usage( + completion_tokens=response_object["usage"].get("completion_tokens", 0), + prompt_tokens=response_object["usage"].get("prompt_tokens", 0), + total_tokens=response_object["usage"].get("total_tokens", 0), + ), + ) + + if "id" in response_object: + model_response_object.id = response_object["id"] + + if "created" in response_object: + model_response_object.created = response_object["created"] + + if "system_fingerprint" in response_object: + model_response_object.system_fingerprint = response_object["system_fingerprint"] + + if "model" in response_object: + model_response_object.model = response_object["model"] + + yield model_response_object + await asyncio.sleep(0) + + +def convert_to_streaming_response(response_object: Optional[dict] = None): + # used for yielding Cache hits when stream == True + if response_object is None: + raise Exception("Error in response object format") + + model_response_object = ModelResponse(stream=True) + choice_list = [] + for idx, choice in enumerate(response_object["choices"]): + delta = Delta( + content=choice["message"].get("content", None), + role=choice["message"]["role"], + function_call=choice["message"].get("function_call", None), + tool_calls=choice["message"].get("tool_calls", None), + ) + finish_reason = choice.get("finish_reason", None) + if finish_reason is None: + # gpt-4 vision can return 'finish_reason' or 'finish_details' + finish_reason = choice.get("finish_details") + logprobs = choice.get("logprobs", None) + enhancements = choice.get("enhancements", None) + choice = StreamingChoices( + finish_reason=finish_reason, + index=idx, + delta=delta, + logprobs=logprobs, + enhancements=enhancements, + ) + + choice_list.append(choice) + model_response_object.choices = choice_list + + if "usage" in response_object and response_object["usage"] is not None: + setattr(model_response_object, "usage", Usage()) + model_response_object.usage.completion_tokens = response_object["usage"].get("completion_tokens", 0) # type: ignore + model_response_object.usage.prompt_tokens = response_object["usage"].get("prompt_tokens", 0) # type: ignore + model_response_object.usage.total_tokens = response_object["usage"].get("total_tokens", 0) # type: ignore + + if "id" in response_object: + model_response_object.id = response_object["id"] + + if "created" in response_object: + model_response_object.created = response_object["created"] + + if "system_fingerprint" in response_object: + model_response_object.system_fingerprint = response_object["system_fingerprint"] + + if "model" in response_object: + model_response_object.model = response_object["model"] + yield model_response_object + + +from collections import defaultdict + + +def _handle_invalid_parallel_tool_calls( + tool_calls: List[ChatCompletionMessageToolCall], +): + """ + Handle hallucinated parallel tool call from openai - https://community.openai.com/t/model-tries-to-call-unknown-function-multi-tool-use-parallel/490653 + + Code modified from: https://github.com/phdowling/openai_multi_tool_use_parallel_patch/blob/main/openai_multi_tool_use_parallel_patch.py + """ + + if tool_calls is None: + return + try: + replacements: Dict[int, List[ChatCompletionMessageToolCall]] = defaultdict(list) + for i, tool_call in enumerate(tool_calls): + current_function = tool_call.function.name + function_args = json.loads(tool_call.function.arguments) + if current_function == "multi_tool_use.parallel": + verbose_logger.debug( + "OpenAI did a weird pseudo-multi-tool-use call, fixing call structure.." + ) + for _fake_i, _fake_tool_use in enumerate(function_args["tool_uses"]): + _function_args = _fake_tool_use["parameters"] + _current_function = _fake_tool_use["recipient_name"] + if _current_function.startswith("functions."): + _current_function = _current_function[len("functions.") :] + + fixed_tc = ChatCompletionMessageToolCall( + id=f"{tool_call.id}_{_fake_i}", + type="function", + function=Function( + name=_current_function, arguments=json.dumps(_function_args) + ), + ) + replacements[i].append(fixed_tc) + + shift = 0 + for i, replacement in replacements.items(): + tool_calls[:] = ( + tool_calls[: i + shift] + replacement + tool_calls[i + shift + 1 :] + ) + shift += len(replacement) + + return tool_calls + except json.JSONDecodeError: + # if there is a JSONDecodeError, return the original tool_calls + return tool_calls + + +def convert_to_model_response_object( # noqa: PLR0915 + response_object: Optional[dict] = None, + model_response_object: Optional[ + Union[ + ModelResponse, + EmbeddingResponse, + ImageResponse, + TranscriptionResponse, + RerankResponse, + ] + ] = None, + response_type: Literal[ + "completion", "embedding", "image_generation", "audio_transcription", "rerank" + ] = "completion", + stream=False, + start_time=None, + end_time=None, + hidden_params: Optional[dict] = None, + _response_headers: Optional[dict] = None, + convert_tool_call_to_json_mode: Optional[ + bool + ] = None, # used for supporting 'json_schema' on older models +): + received_args = locals() + + additional_headers = get_response_headers(_response_headers) + + if hidden_params is None: + hidden_params = {} + hidden_params["additional_headers"] = additional_headers + + ### CHECK IF ERROR IN RESPONSE ### - openrouter returns these in the dictionary + if ( + response_object is not None + and "error" in response_object + and response_object["error"] is not None + ): + error_args = {"status_code": 422, "message": "Error in response object"} + if isinstance(response_object["error"], dict): + if "code" in response_object["error"]: + error_args["status_code"] = response_object["error"]["code"] + if "message" in response_object["error"]: + if isinstance(response_object["error"]["message"], dict): + message_str = json.dumps(response_object["error"]["message"]) + else: + message_str = str(response_object["error"]["message"]) + error_args["message"] = message_str + raised_exception = Exception() + setattr(raised_exception, "status_code", error_args["status_code"]) + setattr(raised_exception, "message", error_args["message"]) + raise raised_exception + + try: + if response_type == "completion" and ( + model_response_object is None + or isinstance(model_response_object, ModelResponse) + ): + if response_object is None or model_response_object is None: + raise Exception("Error in response object format") + if stream is True: + # for returning cached responses, we need to yield a generator + return convert_to_streaming_response(response_object=response_object) + choice_list = [] + + assert response_object["choices"] is not None and isinstance( + response_object["choices"], Iterable + ) + + for idx, choice in enumerate(response_object["choices"]): + ## HANDLE JSON MODE - anthropic returns single function call] + tool_calls = choice["message"].get("tool_calls", None) + if tool_calls is not None: + _openai_tool_calls = [] + for _tc in tool_calls: + _openai_tc = ChatCompletionMessageToolCall(**_tc) + _openai_tool_calls.append(_openai_tc) + fixed_tool_calls = _handle_invalid_parallel_tool_calls( + _openai_tool_calls + ) + + if fixed_tool_calls is not None: + tool_calls = fixed_tool_calls + + message: Optional[Message] = None + finish_reason: Optional[str] = None + if ( + convert_tool_call_to_json_mode + and tool_calls is not None + and len(tool_calls) == 1 + ): + # to support 'json_schema' logic on older models + json_mode_content_str: Optional[str] = tool_calls[0][ + "function" + ].get("arguments") + if json_mode_content_str is not None: + message = litellm.Message(content=json_mode_content_str) + finish_reason = "stop" + if message is None: + message = Message( + content=choice["message"].get("content", None), + role=choice["message"]["role"] or "assistant", + function_call=choice["message"].get("function_call", None), + tool_calls=tool_calls, + audio=choice["message"].get("audio", None), + ) + finish_reason = choice.get("finish_reason", None) + if finish_reason is None: + # gpt-4 vision can return 'finish_reason' or 'finish_details' + finish_reason = choice.get("finish_details") or "stop" + logprobs = choice.get("logprobs", None) + enhancements = choice.get("enhancements", None) + choice = Choices( + finish_reason=finish_reason, + index=idx, + message=message, + logprobs=logprobs, + enhancements=enhancements, + ) + choice_list.append(choice) + model_response_object.choices = choice_list + + if "usage" in response_object and response_object["usage"] is not None: + usage_object = litellm.Usage(**response_object["usage"]) + setattr(model_response_object, "usage", usage_object) + if "created" in response_object: + model_response_object.created = response_object["created"] or int( + time.time() + ) + + if "id" in response_object: + model_response_object.id = response_object["id"] or str(uuid.uuid4()) + + if "system_fingerprint" in response_object: + model_response_object.system_fingerprint = response_object[ + "system_fingerprint" + ] + + if "model" in response_object: + if model_response_object.model is None: + model_response_object.model = response_object["model"] + elif ( + "/" in model_response_object.model + and response_object["model"] is not None + ): + openai_compatible_provider = model_response_object.model.split("/")[ + 0 + ] + model_response_object.model = ( + openai_compatible_provider + "/" + response_object["model"] + ) + + if start_time is not None and end_time is not None: + if isinstance(start_time, type(end_time)): + model_response_object._response_ms = ( # type: ignore + end_time - start_time + ).total_seconds() * 1000 + + if hidden_params is not None: + if model_response_object._hidden_params is None: + model_response_object._hidden_params = {} + model_response_object._hidden_params.update(hidden_params) + + if _response_headers is not None: + model_response_object._response_headers = _response_headers + + special_keys = list(litellm.ModelResponse.model_fields.keys()) + special_keys.append("usage") + for k, v in response_object.items(): + if k not in special_keys: + setattr(model_response_object, k, v) + + return model_response_object + elif response_type == "embedding" and ( + model_response_object is None + or isinstance(model_response_object, EmbeddingResponse) + ): + if response_object is None: + raise Exception("Error in response object format") + + if model_response_object is None: + model_response_object = EmbeddingResponse() + + if "model" in response_object: + model_response_object.model = response_object["model"] + + if "object" in response_object: + model_response_object.object = response_object["object"] + + model_response_object.data = response_object["data"] + + if "usage" in response_object and response_object["usage"] is not None: + model_response_object.usage.completion_tokens = response_object["usage"].get("completion_tokens", 0) # type: ignore + model_response_object.usage.prompt_tokens = response_object["usage"].get("prompt_tokens", 0) # type: ignore + model_response_object.usage.total_tokens = response_object["usage"].get("total_tokens", 0) # type: ignore + + if start_time is not None and end_time is not None: + model_response_object._response_ms = ( # type: ignore + end_time - start_time + ).total_seconds() * 1000 # return response latency in ms like openai + + if hidden_params is not None: + model_response_object._hidden_params = hidden_params + + if _response_headers is not None: + model_response_object._response_headers = _response_headers + + return model_response_object + elif response_type == "image_generation" and ( + model_response_object is None + or isinstance(model_response_object, ImageResponse) + ): + if response_object is None: + raise Exception("Error in response object format") + + if model_response_object is None: + model_response_object = ImageResponse() + + if "created" in response_object: + model_response_object.created = response_object["created"] + + if "data" in response_object: + model_response_object.data = response_object["data"] + + if hidden_params is not None: + model_response_object._hidden_params = hidden_params + + return model_response_object + elif response_type == "audio_transcription" and ( + model_response_object is None + or isinstance(model_response_object, TranscriptionResponse) + ): + if response_object is None: + raise Exception("Error in response object format") + + if model_response_object is None: + model_response_object = TranscriptionResponse() + + if "text" in response_object: + model_response_object.text = response_object["text"] + + optional_keys = ["language", "task", "duration", "words", "segments"] + for key in optional_keys: # not guaranteed to be in response + if key in response_object: + setattr(model_response_object, key, response_object[key]) + + if hidden_params is not None: + model_response_object._hidden_params = hidden_params + + if _response_headers is not None: + model_response_object._response_headers = _response_headers + + return model_response_object + elif response_type == "rerank" and ( + model_response_object is None + or isinstance(model_response_object, RerankResponse) + ): + if response_object is None: + raise Exception("Error in response object format") + + if model_response_object is None: + model_response_object = RerankResponse(**response_object) + return model_response_object + + if "id" in response_object: + model_response_object.id = response_object["id"] + + if "meta" in response_object: + model_response_object.meta = response_object["meta"] + + if "results" in response_object: + model_response_object.results = response_object["results"] + + return model_response_object + except Exception: + raise Exception( + f"Invalid response object {traceback.format_exc()}\n\nreceived_args={received_args}" + ) diff --git a/litellm/utils.py b/litellm/utils.py index 6b3f0a80ca..dc190bc1a8 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -70,6 +70,12 @@ from litellm.litellm_core_utils.get_llm_provider_logic import ( get_llm_provider, ) from litellm.litellm_core_utils.llm_request_utils import _ensure_extra_body_is_safe +from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( + _handle_invalid_parallel_tool_calls, + convert_to_model_response_object, + convert_to_streaming_response, + convert_to_streaming_response_async, +) from litellm.litellm_core_utils.llm_response_utils.get_headers import ( get_response_headers, ) @@ -5494,473 +5500,6 @@ def validate_environment( # noqa: PLR0915 return {"keys_in_environment": keys_in_environment, "missing_keys": missing_keys} -async def convert_to_streaming_response_async(response_object: Optional[dict] = None): - """ - Asynchronously converts a response object to a streaming response. - - Args: - response_object (Optional[dict]): The response object to be converted. Defaults to None. - - Raises: - Exception: If the response object is None. - - Yields: - ModelResponse: The converted streaming response object. - - Returns: - None - """ - if response_object is None: - raise Exception("Error in response object format") - - model_response_object = ModelResponse(stream=True) - - if model_response_object is None: - raise Exception("Error in response creating model response object") - - choice_list = [] - - for idx, choice in enumerate(response_object["choices"]): - if ( - choice["message"].get("tool_calls", None) is not None - and isinstance(choice["message"]["tool_calls"], list) - and len(choice["message"]["tool_calls"]) > 0 - and isinstance(choice["message"]["tool_calls"][0], dict) - ): - pydantic_tool_calls = [] - for index, t in enumerate(choice["message"]["tool_calls"]): - if "index" not in t: - t["index"] = index - pydantic_tool_calls.append(ChatCompletionDeltaToolCall(**t)) - choice["message"]["tool_calls"] = pydantic_tool_calls - delta = Delta( - content=choice["message"].get("content", None), - role=choice["message"]["role"], - function_call=choice["message"].get("function_call", None), - tool_calls=choice["message"].get("tool_calls", None), - ) - finish_reason = choice.get("finish_reason", None) - - if finish_reason is None: - finish_reason = choice.get("finish_details") - - logprobs = choice.get("logprobs", None) - - choice = StreamingChoices( - finish_reason=finish_reason, index=idx, delta=delta, logprobs=logprobs - ) - choice_list.append(choice) - - model_response_object.choices = choice_list - - if "usage" in response_object and response_object["usage"] is not None: - setattr( - model_response_object, - "usage", - Usage( - completion_tokens=response_object["usage"].get("completion_tokens", 0), - prompt_tokens=response_object["usage"].get("prompt_tokens", 0), - total_tokens=response_object["usage"].get("total_tokens", 0), - ), - ) - - if "id" in response_object: - model_response_object.id = response_object["id"] - - if "created" in response_object: - model_response_object.created = response_object["created"] - - if "system_fingerprint" in response_object: - model_response_object.system_fingerprint = response_object["system_fingerprint"] - - if "model" in response_object: - model_response_object.model = response_object["model"] - - yield model_response_object - await asyncio.sleep(0) - - -def convert_to_streaming_response(response_object: Optional[dict] = None): - # used for yielding Cache hits when stream == True - if response_object is None: - raise Exception("Error in response object format") - - model_response_object = ModelResponse(stream=True) - choice_list = [] - for idx, choice in enumerate(response_object["choices"]): - delta = Delta( - content=choice["message"].get("content", None), - role=choice["message"]["role"], - function_call=choice["message"].get("function_call", None), - tool_calls=choice["message"].get("tool_calls", None), - ) - finish_reason = choice.get("finish_reason", None) - if finish_reason is None: - # gpt-4 vision can return 'finish_reason' or 'finish_details' - finish_reason = choice.get("finish_details") - logprobs = choice.get("logprobs", None) - enhancements = choice.get("enhancements", None) - choice = StreamingChoices( - finish_reason=finish_reason, - index=idx, - delta=delta, - logprobs=logprobs, - enhancements=enhancements, - ) - - choice_list.append(choice) - model_response_object.choices = choice_list - - if "usage" in response_object and response_object["usage"] is not None: - setattr(model_response_object, "usage", Usage()) - model_response_object.usage.completion_tokens = response_object["usage"].get("completion_tokens", 0) # type: ignore - model_response_object.usage.prompt_tokens = response_object["usage"].get("prompt_tokens", 0) # type: ignore - model_response_object.usage.total_tokens = response_object["usage"].get("total_tokens", 0) # type: ignore - - if "id" in response_object: - model_response_object.id = response_object["id"] - - if "created" in response_object: - model_response_object.created = response_object["created"] - - if "system_fingerprint" in response_object: - model_response_object.system_fingerprint = response_object["system_fingerprint"] - - if "model" in response_object: - model_response_object.model = response_object["model"] - yield model_response_object - - -from collections import defaultdict - - -def _handle_invalid_parallel_tool_calls( - tool_calls: List[ChatCompletionMessageToolCall], -): - """ - Handle hallucinated parallel tool call from openai - https://community.openai.com/t/model-tries-to-call-unknown-function-multi-tool-use-parallel/490653 - - Code modified from: https://github.com/phdowling/openai_multi_tool_use_parallel_patch/blob/main/openai_multi_tool_use_parallel_patch.py - """ - - if tool_calls is None: - return - try: - replacements: Dict[int, List[ChatCompletionMessageToolCall]] = defaultdict(list) - for i, tool_call in enumerate(tool_calls): - current_function = tool_call.function.name - function_args = json.loads(tool_call.function.arguments) - if current_function == "multi_tool_use.parallel": - verbose_logger.debug( - "OpenAI did a weird pseudo-multi-tool-use call, fixing call structure.." - ) - for _fake_i, _fake_tool_use in enumerate(function_args["tool_uses"]): - _function_args = _fake_tool_use["parameters"] - _current_function = _fake_tool_use["recipient_name"] - if _current_function.startswith("functions."): - _current_function = _current_function[len("functions.") :] - - fixed_tc = ChatCompletionMessageToolCall( - id=f"{tool_call.id}_{_fake_i}", - type="function", - function=Function( - name=_current_function, arguments=json.dumps(_function_args) - ), - ) - replacements[i].append(fixed_tc) - - shift = 0 - for i, replacement in replacements.items(): - tool_calls[:] = ( - tool_calls[: i + shift] + replacement + tool_calls[i + shift + 1 :] - ) - shift += len(replacement) - - return tool_calls - except json.JSONDecodeError: - # if there is a JSONDecodeError, return the original tool_calls - return tool_calls - - -def convert_to_model_response_object( # noqa: PLR0915 - response_object: Optional[dict] = None, - model_response_object: Optional[ - Union[ - ModelResponse, - EmbeddingResponse, - ImageResponse, - TranscriptionResponse, - RerankResponse, - ] - ] = None, - response_type: Literal[ - "completion", "embedding", "image_generation", "audio_transcription", "rerank" - ] = "completion", - stream=False, - start_time=None, - end_time=None, - hidden_params: Optional[dict] = None, - _response_headers: Optional[dict] = None, - convert_tool_call_to_json_mode: Optional[ - bool - ] = None, # used for supporting 'json_schema' on older models -): - received_args = locals() - - additional_headers = get_response_headers(_response_headers) - - if hidden_params is None: - hidden_params = {} - hidden_params["additional_headers"] = additional_headers - - ### CHECK IF ERROR IN RESPONSE ### - openrouter returns these in the dictionary - if ( - response_object is not None - and "error" in response_object - and response_object["error"] is not None - ): - error_args = {"status_code": 422, "message": "Error in response object"} - if isinstance(response_object["error"], dict): - if "code" in response_object["error"]: - error_args["status_code"] = response_object["error"]["code"] - if "message" in response_object["error"]: - if isinstance(response_object["error"]["message"], dict): - message_str = json.dumps(response_object["error"]["message"]) - else: - message_str = str(response_object["error"]["message"]) - error_args["message"] = message_str - raised_exception = Exception() - setattr(raised_exception, "status_code", error_args["status_code"]) - setattr(raised_exception, "message", error_args["message"]) - raise raised_exception - - try: - if response_type == "completion" and ( - model_response_object is None - or isinstance(model_response_object, ModelResponse) - ): - if response_object is None or model_response_object is None: - raise Exception("Error in response object format") - if stream is True: - # for returning cached responses, we need to yield a generator - return convert_to_streaming_response(response_object=response_object) - choice_list = [] - - assert response_object["choices"] is not None and isinstance( - response_object["choices"], Iterable - ) - - for idx, choice in enumerate(response_object["choices"]): - ## HANDLE JSON MODE - anthropic returns single function call] - tool_calls = choice["message"].get("tool_calls", None) - if tool_calls is not None: - _openai_tool_calls = [] - for _tc in tool_calls: - _openai_tc = ChatCompletionMessageToolCall(**_tc) - _openai_tool_calls.append(_openai_tc) - fixed_tool_calls = _handle_invalid_parallel_tool_calls( - _openai_tool_calls - ) - - if fixed_tool_calls is not None: - tool_calls = fixed_tool_calls - - message: Optional[Message] = None - finish_reason: Optional[str] = None - if ( - convert_tool_call_to_json_mode - and tool_calls is not None - and len(tool_calls) == 1 - ): - # to support 'json_schema' logic on older models - json_mode_content_str: Optional[str] = tool_calls[0][ - "function" - ].get("arguments") - if json_mode_content_str is not None: - message = litellm.Message(content=json_mode_content_str) - finish_reason = "stop" - if message is None: - message = Message( - content=choice["message"].get("content", None), - role=choice["message"]["role"] or "assistant", - function_call=choice["message"].get("function_call", None), - tool_calls=tool_calls, - audio=choice["message"].get("audio", None), - ) - finish_reason = choice.get("finish_reason", None) - if finish_reason is None: - # gpt-4 vision can return 'finish_reason' or 'finish_details' - finish_reason = choice.get("finish_details") or "stop" - logprobs = choice.get("logprobs", None) - enhancements = choice.get("enhancements", None) - choice = Choices( - finish_reason=finish_reason, - index=idx, - message=message, - logprobs=logprobs, - enhancements=enhancements, - ) - choice_list.append(choice) - model_response_object.choices = choice_list - - if "usage" in response_object and response_object["usage"] is not None: - usage_object = litellm.Usage(**response_object["usage"]) - setattr(model_response_object, "usage", usage_object) - if "created" in response_object: - model_response_object.created = response_object["created"] or int( - time.time() - ) - - if "id" in response_object: - model_response_object.id = response_object["id"] or str(uuid.uuid4()) - - if "system_fingerprint" in response_object: - model_response_object.system_fingerprint = response_object[ - "system_fingerprint" - ] - - if "model" in response_object: - if model_response_object.model is None: - model_response_object.model = response_object["model"] - elif ( - "/" in model_response_object.model - and response_object["model"] is not None - ): - openai_compatible_provider = model_response_object.model.split("/")[ - 0 - ] - model_response_object.model = ( - openai_compatible_provider + "/" + response_object["model"] - ) - - if start_time is not None and end_time is not None: - if isinstance(start_time, type(end_time)): - model_response_object._response_ms = ( # type: ignore - end_time - start_time - ).total_seconds() * 1000 - - if hidden_params is not None: - if model_response_object._hidden_params is None: - model_response_object._hidden_params = {} - model_response_object._hidden_params.update(hidden_params) - - if _response_headers is not None: - model_response_object._response_headers = _response_headers - - special_keys = list(litellm.ModelResponse.model_fields.keys()) - special_keys.append("usage") - for k, v in response_object.items(): - if k not in special_keys: - setattr(model_response_object, k, v) - - return model_response_object - elif response_type == "embedding" and ( - model_response_object is None - or isinstance(model_response_object, EmbeddingResponse) - ): - if response_object is None: - raise Exception("Error in response object format") - - if model_response_object is None: - model_response_object = EmbeddingResponse() - - if "model" in response_object: - model_response_object.model = response_object["model"] - - if "object" in response_object: - model_response_object.object = response_object["object"] - - model_response_object.data = response_object["data"] - - if "usage" in response_object and response_object["usage"] is not None: - model_response_object.usage.completion_tokens = response_object["usage"].get("completion_tokens", 0) # type: ignore - model_response_object.usage.prompt_tokens = response_object["usage"].get("prompt_tokens", 0) # type: ignore - model_response_object.usage.total_tokens = response_object["usage"].get("total_tokens", 0) # type: ignore - - if start_time is not None and end_time is not None: - model_response_object._response_ms = ( # type: ignore - end_time - start_time - ).total_seconds() * 1000 # return response latency in ms like openai - - if hidden_params is not None: - model_response_object._hidden_params = hidden_params - - if _response_headers is not None: - model_response_object._response_headers = _response_headers - - return model_response_object - elif response_type == "image_generation" and ( - model_response_object is None - or isinstance(model_response_object, ImageResponse) - ): - if response_object is None: - raise Exception("Error in response object format") - - if model_response_object is None: - model_response_object = ImageResponse() - - if "created" in response_object: - model_response_object.created = response_object["created"] - - if "data" in response_object: - model_response_object.data = response_object["data"] - - if hidden_params is not None: - model_response_object._hidden_params = hidden_params - - return model_response_object - elif response_type == "audio_transcription" and ( - model_response_object is None - or isinstance(model_response_object, TranscriptionResponse) - ): - if response_object is None: - raise Exception("Error in response object format") - - if model_response_object is None: - model_response_object = TranscriptionResponse() - - if "text" in response_object: - model_response_object.text = response_object["text"] - - optional_keys = ["language", "task", "duration", "words", "segments"] - for key in optional_keys: # not guaranteed to be in response - if key in response_object: - setattr(model_response_object, key, response_object[key]) - - if hidden_params is not None: - model_response_object._hidden_params = hidden_params - - if _response_headers is not None: - model_response_object._response_headers = _response_headers - - return model_response_object - elif response_type == "rerank" and ( - model_response_object is None - or isinstance(model_response_object, RerankResponse) - ): - if response_object is None: - raise Exception("Error in response object format") - - if model_response_object is None: - model_response_object = RerankResponse(**response_object) - return model_response_object - - if "id" in response_object: - model_response_object.id = response_object["id"] - - if "meta" in response_object: - model_response_object.meta = response_object["meta"] - - if "results" in response_object: - model_response_object.results = response_object["results"] - - return model_response_object - except Exception: - raise Exception( - f"Invalid response object {traceback.format_exc()}\n\nreceived_args={received_args}" - ) - - def acreate(*args, **kwargs): ## Thin client to handle the acreate langchain call return litellm.acompletion(*args, **kwargs) From b70147f63b5ad95d90be371a50c6248fe21b20e8 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 17:33:19 +0530 Subject: [PATCH 19/62] (refactor) litellm.Router client initialization utils (#6394) * refactor InitalizeOpenAISDKClient * use helper func for _should_create_openai_sdk_client_for_model * use static methods for set client on litellm router * reduce LOC in _get_client_initialization_params * fix _should_create_openai_sdk_client_for_model * code quality fix * test test_should_create_openai_sdk_client_for_model * test test_get_client_initialization_params_openai * fix mypy linting errors * fix OpenAISDKClientInitializationParams * test_get_client_initialization_params_all_env_vars * test_get_client_initialization_params_azure_ai_studio_mistral * test_get_client_initialization_params_default_values * fix _get_client_initialization_params --- litellm/router.py | 23 +- .../client_initalization_utils.py | 1051 +++++++++-------- tests/local_testing/test_router_init.py | 284 +++++ 3 files changed, 882 insertions(+), 476 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 142a781bbe..0cad565b00 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -63,10 +63,7 @@ from litellm.router_utils.batch_utils import ( _get_router_metadata_variable_name, replace_model_in_jsonl, ) -from litellm.router_utils.client_initalization_utils import ( - set_client, - should_initialize_sync_client, -) +from litellm.router_utils.client_initalization_utils import InitalizeOpenAISDKClient from litellm.router_utils.cooldown_cache import CooldownCache from litellm.router_utils.cooldown_callbacks import router_cooldown_event_callback from litellm.router_utils.cooldown_handlers import ( @@ -3951,7 +3948,7 @@ class Router: raise Exception(f"Unsupported provider - {custom_llm_provider}") # init OpenAI, Azure clients - set_client( + InitalizeOpenAISDKClient.set_client( litellm_router_instance=self, model=deployment.to_json(exclude_none=True) ) @@ -4661,7 +4658,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4671,7 +4670,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4682,7 +4683,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key) return client else: @@ -4692,7 +4695,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key) return client diff --git a/litellm/router_utils/client_initalization_utils.py b/litellm/router_utils/client_initalization_utils.py index 6c845296a8..679cefadfa 100644 --- a/litellm/router_utils/client_initalization_utils.py +++ b/litellm/router_utils/client_initalization_utils.py @@ -1,10 +1,11 @@ import asyncio import os import traceback -from typing import TYPE_CHECKING, Any, Callable, Optional +from typing import TYPE_CHECKING, Any, Callable, Optional, Union import httpx import openai +from pydantic import BaseModel import litellm from litellm import get_secret, get_secret_str @@ -16,89 +17,511 @@ from litellm.secret_managers.get_azure_ad_token_provider import ( from litellm.utils import calculate_max_parallel_requests if TYPE_CHECKING: + from httpx import Timeout as httpxTimeout + from litellm.router import Router as _Router LitellmRouter = _Router else: LitellmRouter = Any + httpxTimeout = Any -def should_initialize_sync_client( - litellm_router_instance: LitellmRouter, -) -> bool: +class OpenAISDKClientInitializationParams(BaseModel): + api_key: Optional[str] + api_base: Optional[str] + api_version: Optional[str] + azure_ad_token_provider: Optional[Callable[[], str]] + timeout: Optional[Union[float, httpxTimeout]] + stream_timeout: Optional[Union[float, httpxTimeout]] + max_retries: int + organization: Optional[str] + + # Internal LiteLLM specific params + custom_llm_provider: Optional[str] + model_name: str + + +class InitalizeOpenAISDKClient: """ - Returns if Sync OpenAI, Azure Clients should be initialized. - - Do not init sync clients when router.router_general_settings.async_only_mode is True - + OpenAI Python SDK requires creating a OpenAI/AzureOpenAI client + this class is responsible for creating that client """ - if litellm_router_instance is None: - return False - if litellm_router_instance.router_general_settings is not None: - if ( - hasattr(litellm_router_instance, "router_general_settings") - and hasattr( - litellm_router_instance.router_general_settings, "async_only_mode" - ) - and litellm_router_instance.router_general_settings.async_only_mode is True - ): + @staticmethod + def should_initialize_sync_client( + litellm_router_instance: LitellmRouter, + ) -> bool: + """ + Returns if Sync OpenAI, Azure Clients should be initialized. + + Do not init sync clients when router.router_general_settings.async_only_mode is True + + """ + if litellm_router_instance is None: return False - return True + if litellm_router_instance.router_general_settings is not None: + if ( + hasattr(litellm_router_instance, "router_general_settings") + and hasattr( + litellm_router_instance.router_general_settings, "async_only_mode" + ) + and litellm_router_instance.router_general_settings.async_only_mode + is True + ): + return False + return True -def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PLR0915 - """ - - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 - - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 - """ - client_ttl = litellm_router_instance.client_ttl - litellm_params = model.get("litellm_params", {}) - model_name = litellm_params.get("model") - model_id = model["model_info"]["id"] - # ### IF RPM SET - initialize a semaphore ### - rpm = litellm_params.get("rpm", None) - tpm = litellm_params.get("tpm", None) - max_parallel_requests = litellm_params.get("max_parallel_requests", None) - calculated_max_parallel_requests = calculate_max_parallel_requests( - rpm=rpm, - max_parallel_requests=max_parallel_requests, - tpm=tpm, - default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, - ) - if calculated_max_parallel_requests: - semaphore = asyncio.Semaphore(calculated_max_parallel_requests) - cache_key = f"{model_id}_max_parallel_requests_client" - litellm_router_instance.cache.set_cache( - key=cache_key, - value=semaphore, - local_only=True, - ) - - #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## - custom_llm_provider = litellm_params.get("custom_llm_provider") - custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" - default_api_base = None - default_api_key = None - if custom_llm_provider in litellm.openai_compatible_providers: - _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( - model=model_name - ) - default_api_base = api_base - default_api_key = api_key - - if ( - model_name in litellm.open_ai_chat_completion_models - or custom_llm_provider in litellm.openai_compatible_providers - or custom_llm_provider == "azure" - or custom_llm_provider == "azure_text" - or custom_llm_provider == "custom_openai" - or custom_llm_provider == "openai" - or custom_llm_provider == "text-completion-openai" - or "ft:gpt-3.5-turbo" in model_name - or model_name in litellm.open_ai_embedding_models + @staticmethod + def set_client( # noqa: PLR0915 + litellm_router_instance: LitellmRouter, model: dict ): + """ + - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 + - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 + """ + client_ttl = litellm_router_instance.client_ttl + litellm_params = model.get("litellm_params", {}) + model_name = litellm_params.get("model") + model_id = model["model_info"]["id"] + # ### IF RPM SET - initialize a semaphore ### + rpm = litellm_params.get("rpm", None) + tpm = litellm_params.get("tpm", None) + max_parallel_requests = litellm_params.get("max_parallel_requests", None) + calculated_max_parallel_requests = calculate_max_parallel_requests( + rpm=rpm, + max_parallel_requests=max_parallel_requests, + tpm=tpm, + default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, + ) + if calculated_max_parallel_requests: + semaphore = asyncio.Semaphore(calculated_max_parallel_requests) + cache_key = f"{model_id}_max_parallel_requests_client" + litellm_router_instance.cache.set_cache( + key=cache_key, + value=semaphore, + local_only=True, + ) + + #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## + custom_llm_provider = litellm_params.get("custom_llm_provider") + custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" + default_api_base = None + default_api_key = None + if custom_llm_provider in litellm.openai_compatible_providers: + _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( + model=model_name + ) + default_api_base = api_base + default_api_key = api_key + + if InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( + model_name=model_name, + custom_llm_provider=custom_llm_provider, + ): + client_initialization_params = ( + InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + ) + + ############### Unpack client initialization params ####################### + api_key = client_initialization_params.api_key + api_base = client_initialization_params.api_base + api_version: Optional[str] = client_initialization_params.api_version + timeout: Optional[Union[float, httpxTimeout]] = ( + client_initialization_params.timeout + ) + stream_timeout: Optional[Union[float, httpxTimeout]] = ( + client_initialization_params.stream_timeout + ) + max_retries: int = client_initialization_params.max_retries + organization: Optional[str] = client_initialization_params.organization + azure_ad_token_provider: Optional[Callable[[], str]] = ( + client_initialization_params.azure_ad_token_provider + ) + custom_llm_provider = client_initialization_params.custom_llm_provider + model_name = client_initialization_params.model_name + ########################################################################## + + if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": + if api_base is None or not isinstance(api_base, str): + filtered_litellm_params = { + k: v + for k, v in model["litellm_params"].items() + if k != "api_key" + } + _filtered_model = { + "model_name": model["model_name"], + "litellm_params": filtered_litellm_params, + } + raise ValueError( + f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" + ) + azure_ad_token = litellm_params.get("azure_ad_token") + if azure_ad_token is not None: + if azure_ad_token.startswith("oidc/"): + azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) + elif ( + azure_ad_token_provider is None + and litellm.enable_azure_ad_token_refresh is True + ): + try: + azure_ad_token_provider = get_azure_ad_token_provider() + except ValueError: + verbose_router_logger.debug( + "Azure AD Token Provider could not be used." + ) + if api_version is None: + api_version = os.getenv( + "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION + ) + + if "gateway.ai.cloudflare.com" in api_base: + if not api_base.endswith("/"): + api_base += "/" + azure_model = model_name.replace("azure/", "") + api_base += f"{azure_model}" + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=timeout, + max_retries=max_retries, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=timeout, + max_retries=max_retries, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + # streaming clients can have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=stream_timeout, + max_retries=max_retries, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=stream_timeout, + max_retries=max_retries, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + else: + _api_key = api_key + if _api_key is not None and isinstance(_api_key, str): + # only show first 5 chars of api_key + _api_key = _api_key[:8] + "*" * 15 + verbose_router_logger.debug( + f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" + ) + azure_client_params = { + "api_key": api_key, + "azure_endpoint": api_base, + "api_version": api_version, + "azure_ad_token": azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider, + } + + if azure_ad_token_provider is not None: + azure_client_params["azure_ad_token_provider"] = ( + azure_ad_token_provider + ) + from litellm.llms.AzureOpenAI.azure import ( + select_azure_base_url_or_endpoint, + ) + + # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client + # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client + azure_client_params = select_azure_base_url_or_endpoint( + azure_client_params + ) + + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, + max_retries=max_retries, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, + max_retries=max_retries, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, + max_retries=max_retries, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, + max_retries=max_retries, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + else: + _api_key = api_key # type: ignore + if _api_key is not None and isinstance(_api_key, str): + # only show first 5 chars of api_key + _api_key = _api_key[:8] + "*" * 15 + verbose_router_logger.debug( + f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" + ) + cache_key = f"{model_id}_async_client" + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=timeout, + max_retries=max_retries, + organization=organization, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=timeout, + max_retries=max_retries, + organization=organization, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=stream_timeout, + max_retries=max_retries, + organization=organization, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_client" + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=stream_timeout, + max_retries=max_retries, + organization=organization, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + @staticmethod + def _get_client_initialization_params( + model: dict, + model_name: str, + custom_llm_provider: Optional[str], + litellm_params: dict, + default_api_key: Optional[str], + default_api_base: Optional[str], + ) -> OpenAISDKClientInitializationParams: + """ + Returns params to use for initializing OpenAI SDK clients (for OpenAI, Azure OpenAI, OpenAI Compatible Providers) + + Args: + model: model dict + model_name: model name + custom_llm_provider: custom llm provider + litellm_params: litellm params + default_api_key: default api key + default_api_base: default api base + + Returns: + OpenAISDKClientInitializationParams + """ + is_azure_ai_studio_model: bool = False if custom_llm_provider == "azure": if litellm.utils._is_non_openai_azure_model(model_name): @@ -111,8 +534,7 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL # we do this here because we init clients for Azure, OpenAI and we need to set the right key api_key = litellm_params.get("api_key") or default_api_key if api_key and isinstance(api_key, str) and api_key.startswith("os.environ/"): - api_key_env_name = api_key.replace("os.environ/", "") - api_key = get_secret_str(api_key_env_name) + api_key = get_secret_str(api_key) litellm_params["api_key"] = api_key api_base = litellm_params.get("api_base") @@ -121,8 +543,7 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL api_base or base_url or default_api_base ) # allow users to pass in `api_base` or `base_url` for azure if api_base and api_base.startswith("os.environ/"): - api_base_env_name = api_base.replace("os.environ/", "") - api_base = get_secret_str(api_base_env_name) + api_base = get_secret_str(api_base) litellm_params["api_base"] = api_base ## AZURE AI STUDIO MISTRAL CHECK ## @@ -147,436 +568,132 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL api_version = litellm_params.get("api_version") if api_version and api_version.startswith("os.environ/"): - api_version_env_name = api_version.replace("os.environ/", "") - api_version = get_secret_str(api_version_env_name) + api_version = get_secret_str(api_version) litellm_params["api_version"] = api_version - timeout: Optional[float] = ( + timeout: Optional[Union[float, str, httpxTimeout]] = ( litellm_params.pop("timeout", None) or litellm.request_timeout ) if isinstance(timeout, str) and timeout.startswith("os.environ/"): - timeout_env_name = timeout.replace("os.environ/", "") - timeout = get_secret(timeout_env_name) # type: ignore + timeout = float(get_secret(timeout)) # type: ignore litellm_params["timeout"] = timeout - stream_timeout: Optional[float] = litellm_params.pop( + stream_timeout: Optional[Union[float, str, httpxTimeout]] = litellm_params.pop( "stream_timeout", timeout ) # if no stream_timeout is set, default to timeout if isinstance(stream_timeout, str) and stream_timeout.startswith("os.environ/"): - stream_timeout_env_name = stream_timeout.replace("os.environ/", "") - stream_timeout = get_secret(stream_timeout_env_name) # type: ignore + stream_timeout = float(get_secret(stream_timeout)) # type: ignore litellm_params["stream_timeout"] = stream_timeout max_retries: Optional[int] = litellm_params.pop( "max_retries", 0 ) # router handles retry logic if isinstance(max_retries, str) and max_retries.startswith("os.environ/"): - max_retries_env_name = max_retries.replace("os.environ/", "") - max_retries = get_secret(max_retries_env_name) # type: ignore + max_retries = get_secret(max_retries) # type: ignore litellm_params["max_retries"] = max_retries organization = litellm_params.get("organization", None) if isinstance(organization, str) and organization.startswith("os.environ/"): - organization_env_name = organization.replace("os.environ/", "") - organization = get_secret_str(organization_env_name) + organization = get_secret_str(organization) litellm_params["organization"] = organization azure_ad_token_provider: Optional[Callable[[], str]] = None - if litellm_params.get("tenant_id"): + tenant_id = litellm_params.get("tenant_id") + if tenant_id is not None: verbose_router_logger.debug("Using Azure AD Token Provider for Azure Auth") - azure_ad_token_provider = get_azure_ad_token_from_entrata_id( - tenant_id=litellm_params.get("tenant_id"), - client_id=litellm_params.get("client_id"), - client_secret=litellm_params.get("client_secret"), + azure_ad_token_provider = ( + InitalizeOpenAISDKClient.get_azure_ad_token_from_entrata_id( + tenant_id=tenant_id, + client_id=litellm_params.get("client_id"), + client_secret=litellm_params.get("client_secret"), + ) ) - if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": - if api_base is None or not isinstance(api_base, str): - filtered_litellm_params = { - k: v for k, v in model["litellm_params"].items() if k != "api_key" - } - _filtered_model = { - "model_name": model["model_name"], - "litellm_params": filtered_litellm_params, - } - raise ValueError( - f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" - ) - azure_ad_token = litellm_params.get("azure_ad_token") - if azure_ad_token is not None: - if azure_ad_token.startswith("oidc/"): - azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) - elif ( - azure_ad_token_provider is None - and litellm.enable_azure_ad_token_refresh is True - ): - try: - azure_ad_token_provider = get_azure_ad_token_provider() - except ValueError: - verbose_router_logger.debug( - "Azure AD Token Provider could not be used." - ) - if api_version is None: - api_version = os.getenv( - "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION - ) + return OpenAISDKClientInitializationParams( + api_key=api_key, + api_base=api_base, + api_version=api_version, + azure_ad_token_provider=azure_ad_token_provider, + timeout=timeout, # type: ignore + stream_timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + organization=organization, + custom_llm_provider=custom_llm_provider, + model_name=model_name, + ) - if "gateway.ai.cloudflare.com" in api_base: - if not api_base.endswith("/"): - api_base += "/" - azure_model = model_name.replace("azure/", "") - api_base += f"{azure_model}" - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + @staticmethod + def _should_create_openai_sdk_client_for_model( + model_name: str, + custom_llm_provider: str, + ) -> bool: + """ + Returns True if a OpenAI SDK client should be created for a given model - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - # streaming clients can have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + We need a OpenAI SDK client for models that are callsed using OpenAI Python SDK + Azure OpenAI, OpenAI, OpenAI Compatible Providers, OpenAI Embedding Models + """ + if ( + model_name in litellm.open_ai_chat_completion_models + or custom_llm_provider in litellm.openai_compatible_providers + or custom_llm_provider == "azure" + or custom_llm_provider == "azure_text" + or custom_llm_provider == "custom_openai" + or custom_llm_provider == "openai" + or custom_llm_provider == "text-completion-openai" + or "ft:gpt-3.5-turbo" in model_name + or model_name in litellm.open_ai_embedding_models + ): + return True + return False - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - else: - _api_key = api_key - if _api_key is not None and isinstance(_api_key, str): - # only show first 5 chars of api_key - _api_key = _api_key[:8] + "*" * 15 - verbose_router_logger.debug( - f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" - ) - azure_client_params = { - "api_key": api_key, - "azure_endpoint": api_base, - "api_version": api_version, - "azure_ad_token": azure_ad_token, - "azure_ad_token_provider": azure_ad_token_provider, - } + @staticmethod + def get_azure_ad_token_from_entrata_id( + tenant_id: str, client_id: Optional[str], client_secret: Optional[str] + ) -> Callable[[], str]: + from azure.identity import ( + ClientSecretCredential, + DefaultAzureCredential, + get_bearer_token_provider, + ) - if azure_ad_token_provider is not None: - azure_client_params["azure_ad_token_provider"] = ( - azure_ad_token_provider - ) - from litellm.llms.AzureOpenAI.azure import ( - select_azure_base_url_or_endpoint, - ) + if client_id is None or client_secret is None: + raise ValueError( + "client_id and client_secret must be provided when using `tenant_id`" + ) - # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client - # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client - azure_client_params = select_azure_base_url_or_endpoint( - azure_client_params - ) - - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") + if tenant_id.startswith("os.environ/"): + _tenant_id = get_secret_str(tenant_id) else: - _api_key = api_key # type: ignore - if _api_key is not None and isinstance(_api_key, str): - # only show first 5 chars of api_key - _api_key = _api_key[:8] + "*" * 15 - verbose_router_logger.debug( - f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" - ) - cache_key = f"{model_id}_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + _tenant_id = tenant_id - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + if client_id.startswith("os.environ/"): + _client_id = get_secret_str(client_id) + else: + _client_id = client_id - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + if client_secret.startswith("os.environ/"): + _client_secret = get_secret_str(client_secret) + else: + _client_secret = client_secret - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + verbose_router_logger.debug( + "tenant_id %s, client_id %s, client_secret %s", + _tenant_id, + _client_id, + _client_secret, + ) + if _tenant_id is None or _client_id is None or _client_secret is None: + raise ValueError("tenant_id, client_id, and client_secret must be provided") + credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) + verbose_router_logger.debug("credential %s", credential) -def get_azure_ad_token_from_entrata_id( - tenant_id: str, client_id: str, client_secret: str -) -> Callable[[], str]: - from azure.identity import ( - ClientSecretCredential, - DefaultAzureCredential, - get_bearer_token_provider, - ) + token_provider = get_bearer_token_provider( + credential, "https://cognitiveservices.azure.com/.default" + ) - verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") + verbose_router_logger.debug("token_provider %s", token_provider) - if tenant_id.startswith("os.environ/"): - _tenant_id = get_secret_str(tenant_id) - else: - _tenant_id = tenant_id - - if client_id.startswith("os.environ/"): - _client_id = get_secret_str(client_id) - else: - _client_id = client_id - - if client_secret.startswith("os.environ/"): - _client_secret = get_secret_str(client_secret) - else: - _client_secret = client_secret - - verbose_router_logger.debug( - "tenant_id %s, client_id %s, client_secret %s", - _tenant_id, - _client_id, - _client_secret, - ) - if _tenant_id is None or _client_id is None or _client_secret is None: - raise ValueError("tenant_id, client_id, and client_secret must be provided") - credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) - - verbose_router_logger.debug("credential %s", credential) - - token_provider = get_bearer_token_provider( - credential, "https://cognitiveservices.azure.com/.default" - ) - - verbose_router_logger.debug("token_provider %s", token_provider) - - return token_provider + return token_provider diff --git a/tests/local_testing/test_router_init.py b/tests/local_testing/test_router_init.py index 3733af252b..45695f3c3d 100644 --- a/tests/local_testing/test_router_init.py +++ b/tests/local_testing/test_router_init.py @@ -17,6 +17,10 @@ from dotenv import load_dotenv import litellm from litellm import Router +from litellm.router_utils.client_initalization_utils import ( + InitalizeOpenAISDKClient, + OpenAISDKClientInitializationParams, +) load_dotenv() @@ -696,3 +700,283 @@ def test_init_router_with_supported_environments(environment, expected_models): assert set(_model_list) == set(expected_models) os.environ.pop("LITELLM_ENVIRONMENT") + + +def test_should_initialize_sync_client(): + from litellm.types.router import RouterGeneralSettings + + # Test case 1: Router instance is None + assert InitalizeOpenAISDKClient.should_initialize_sync_client(None) is False + + # Test case 2: Router instance without router_general_settings + router = Router(model_list=[]) + assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True + + # Test case 3: Router instance with async_only_mode = False + router = Router( + model_list=[], + router_general_settings=RouterGeneralSettings(async_only_mode=False), + ) + assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True + + # Test case 4: Router instance with async_only_mode = True + router = Router( + model_list=[], + router_general_settings=RouterGeneralSettings(async_only_mode=True), + ) + assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is False + + # Test case 5: Router instance with router_general_settings but without async_only_mode + router = Router(model_list=[], router_general_settings=RouterGeneralSettings()) + assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True + + print("All test cases passed!") + + +@pytest.mark.parametrize( + "model_name, custom_llm_provider, expected_result", + [ + ("gpt-3.5-turbo", None, True), # OpenAI chat completion model + ("text-embedding-ada-002", None, True), # OpenAI embedding model + ("claude-2", None, False), # Non-OpenAI model + ("gpt-3.5-turbo", "azure", True), # Azure OpenAI + ("text-davinci-003", "azure_text", True), # Azure OpenAI + ("gpt-3.5-turbo", "openai", True), # OpenAI + ("custom-model", "custom_openai", True), # Custom OpenAI compatible + ("text-davinci-003", "text-completion-openai", True), # OpenAI text completion + ( + "ft:gpt-3.5-turbo-0613:my-org:custom-model:7p4lURel", + None, + True, + ), # Fine-tuned GPT model + ("mistral-7b", "huggingface", False), # Non-OpenAI provider + ("custom-model", "anthropic", False), # Non-OpenAI compatible provider + ], +) +def test_should_create_openai_sdk_client_for_model( + model_name, custom_llm_provider, expected_result +): + result = InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( + model_name, custom_llm_provider + ) + assert ( + result == expected_result + ), f"Failed for model: {model_name}, provider: {custom_llm_provider}" + + +def test_should_create_openai_sdk_client_for_model_openai_compatible_providers(): + # Test with a known OpenAI compatible provider + assert InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( + "custom-model", "groq" + ), "Should return True for OpenAI compatible provider" + + # Add a new compatible provider and test + litellm.openai_compatible_providers.append("new_provider") + assert InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( + "custom-model", "new_provider" + ), "Should return True for newly added OpenAI compatible provider" + + # Clean up + litellm.openai_compatible_providers.remove("new_provider") + + +def test_get_client_initialization_params_openai(): + """Test basic OpenAI configuration with direct parameter passing.""" + model = {} + model_name = "gpt-3.5-turbo" + custom_llm_provider = None + litellm_params = {"api_key": "sk-openai-key", "timeout": 30, "max_retries": 3} + default_api_key = None + default_api_base = None + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + + assert isinstance(result, OpenAISDKClientInitializationParams) + assert result.api_key == "sk-openai-key" + assert result.timeout == 30 + assert result.max_retries == 3 + assert result.model_name == "gpt-3.5-turbo" + + +def test_get_client_initialization_params_azure(): + """Test Azure OpenAI configuration with specific Azure parameters.""" + model = {} + model_name = "azure/gpt-4" + custom_llm_provider = "azure" + litellm_params = { + "api_key": "azure-key", + "api_base": "https://example.azure.openai.com", + "api_version": "2023-05-15", + } + default_api_key = None + default_api_base = None + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + + assert result.api_key == "azure-key" + assert result.api_base == "https://example.azure.openai.com" + assert result.api_version == "2023-05-15" + assert result.custom_llm_provider == "azure" + + +def test_get_client_initialization_params_environment_variable_parsing(): + """Test parsing of environment variables for configuration.""" + os.environ["UNIQUE_OPENAI_API_KEY"] = "env-openai-key" + os.environ["UNIQUE_TIMEOUT"] = "45" + + model = {} + model_name = "gpt-4" + custom_llm_provider = None + litellm_params = { + "api_key": "os.environ/UNIQUE_OPENAI_API_KEY", + "timeout": "os.environ/UNIQUE_TIMEOUT", + "organization": "os.environ/UNIQUE_ORG_ID", + } + default_api_key = None + default_api_base = None + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + + assert result.api_key == "env-openai-key" + assert result.timeout == 45.0 + assert result.organization is None # Since ORG_ID is not set in the environment + + +def test_get_client_initialization_params_azure_ai_studio_mistral(): + """ + Test configuration for Azure AI Studio Mistral model. + + - /v1/ is added to the api_base if it is not present + - custom_llm_provider is set to openai (Azure AI Studio Mistral models need to use OpenAI route) + """ + + model = {} + model_name = "azure/mistral-large-latest" + custom_llm_provider = "azure" + litellm_params = { + "api_key": "azure-key", + "api_base": "https://example.azure.openai.com", + } + default_api_key = None + default_api_base = None + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model, + model_name, + custom_llm_provider, + litellm_params, + default_api_key, + default_api_base, + ) + + assert result.custom_llm_provider == "openai" + assert result.model_name == "mistral-large-latest" + assert result.api_base == "https://example.azure.openai.com/v1/" + + +def test_get_client_initialization_params_default_values(): + """ + Test use of default values when specific parameters are not provided. + + This is used typically for OpenAI compatible providers - example Together AI + + """ + model = {} + model_name = "together/meta-llama-3.1-8b-instruct" + custom_llm_provider = None + litellm_params = {} + default_api_key = "together-api-key" + default_api_base = "https://together.xyz/api.openai.com" + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + + assert result.api_key == "together-api-key" + assert result.api_base == "https://together.xyz/api.openai.com" + assert result.timeout == litellm.request_timeout + assert result.max_retries == 0 + + +def test_get_client_initialization_params_all_env_vars(): + # Set up environment variables + os.environ["TEST_API_KEY"] = "test-api-key" + os.environ["TEST_API_BASE"] = "https://test.openai.com" + os.environ["TEST_API_VERSION"] = "2023-05-15" + os.environ["TEST_TIMEOUT"] = "30" + os.environ["TEST_STREAM_TIMEOUT"] = "60" + os.environ["TEST_MAX_RETRIES"] = "3" + os.environ["TEST_ORGANIZATION"] = "test-org" + + model = {} + model_name = "gpt-4" + custom_llm_provider = None + litellm_params = { + "api_key": "os.environ/TEST_API_KEY", + "api_base": "os.environ/TEST_API_BASE", + "api_version": "os.environ/TEST_API_VERSION", + "timeout": "os.environ/TEST_TIMEOUT", + "stream_timeout": "os.environ/TEST_STREAM_TIMEOUT", + "max_retries": "os.environ/TEST_MAX_RETRIES", + "organization": "os.environ/TEST_ORGANIZATION", + } + default_api_key = None + default_api_base = None + + result = InitalizeOpenAISDKClient._get_client_initialization_params( + model=model, + model_name=model_name, + custom_llm_provider=custom_llm_provider, + litellm_params=litellm_params, + default_api_key=default_api_key, + default_api_base=default_api_base, + ) + + assert isinstance(result, OpenAISDKClientInitializationParams) + assert result.api_key == "test-api-key" + assert result.api_base == "https://test.openai.com" + assert result.api_version == "2023-05-15" + assert result.timeout == 30.0 + assert result.stream_timeout == 60.0 + assert result.max_retries == 3 + assert result.organization == "test-org" + assert result.model_name == "gpt-4" + assert result.custom_llm_provider is None + + # Clean up environment variables + for key in [ + "TEST_API_KEY", + "TEST_API_BASE", + "TEST_API_VERSION", + "TEST_TIMEOUT", + "TEST_STREAM_TIMEOUT", + "TEST_MAX_RETRIES", + "TEST_ORGANIZATION", + ]: + os.environ.pop(key) From 72a91ea9dd251c04af7d70e4ab9fdaffdf8cc209 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 18:24:22 +0530 Subject: [PATCH 20/62] (fix) Langfuse key based logging (#6372) * langfuse use helper for get_langfuse_logging_config * fix get_langfuse_logger_for_request * fix import * fix get_langfuse_logger_for_request * test_get_langfuse_logger_for_request_with_dynamic_params * unit testing for test_get_langfuse_logger_for_request_with_no_dynamic_params * parameterized langfuse testing * fix langfuse test * fix langfuse logging * fix test_aaalangfuse_logging_metadata * fix langfuse log metadata test * fix langfuse logger * use create_langfuse_logger_from_credentials * fix test_get_langfuse_logger_for_request_with_no_dynamic_params * fix correct langfuse/ folder structure * use static methods for langfuse logger * add commment on langfuse handler * fix linting error * add unit testing for langfuse logging * fix linting * fix failure handler langfuse --- .../integrations/{ => langfuse}/langfuse.py | 57 +---- .../integrations/langfuse/langfuse_handler.py | 168 ++++++++++++++ litellm/litellm_core_utils/litellm_logging.py | 128 ++-------- .../health_endpoints/_health_endpoints.py | 2 +- litellm/proxy/proxy_config.yaml | 54 ++--- tests/local_testing/test_alangfuse.py | 8 +- .../test_langfuse_unit_tests.py | 219 ++++++++++++++++++ 7 files changed, 426 insertions(+), 210 deletions(-) rename litellm/integrations/{ => langfuse}/langfuse.py (93%) create mode 100644 litellm/integrations/langfuse/langfuse_handler.py create mode 100644 tests/logging_callback_tests/test_langfuse_unit_tests.py diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse/langfuse.py similarity index 93% rename from litellm/integrations/langfuse.py rename to litellm/integrations/langfuse/langfuse.py index fa52b3d7a2..182c886376 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse/langfuse.py @@ -4,7 +4,7 @@ import copy import inspect import os import traceback -from typing import Any, Dict, Optional +from typing import TYPE_CHECKING, Any, Dict, Optional from packaging.version import Version from pydantic import BaseModel @@ -16,6 +16,11 @@ from litellm.secret_managers.main import str_to_bool from litellm.types.integrations.langfuse import * from litellm.types.utils import StandardCallbackDynamicParams, StandardLoggingPayload +if TYPE_CHECKING: + from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache +else: + DynamicLoggingCache = Any + class LangFuseLogger: # Class variables or attributes @@ -798,53 +803,3 @@ def log_requester_metadata(clean_metadata: dict): returned_metadata.update({"requester_metadata": requester_metadata}) return returned_metadata - - -def get_langfuse_logging_config( - standard_callback_dynamic_params: StandardCallbackDynamicParams, - globalLangfuseLogger: Optional[LangFuseLogger] = None, -) -> LangfuseLoggingConfig: - """ - This function is used to get the Langfuse logging config for the Langfuse Logger. - It checks if the dynamic parameters are provided in the standard_callback_dynamic_params and uses them to get the Langfuse logging config. - - If no dynamic parameters are provided, it uses the default values. - """ - # only use dynamic params if langfuse credentials are passed dynamically - if _dynamic_langfuse_credentials_are_passed(standard_callback_dynamic_params): - return LangfuseLoggingConfig( - langfuse_secret=standard_callback_dynamic_params.get("langfuse_secret") - or standard_callback_dynamic_params.get("langfuse_secret_key"), - langfuse_public_key=standard_callback_dynamic_params.get( - "langfuse_public_key" - ), - langfuse_host=standard_callback_dynamic_params.get("langfuse_host"), - ) - elif globalLangfuseLogger is not None: - return LangfuseLoggingConfig( - langfuse_secret=globalLangfuseLogger.secret_key, - langfuse_public_key=globalLangfuseLogger.public_key, - langfuse_host=globalLangfuseLogger.langfuse_host, - ) - raise ValueError( - "`langfuse` set as a callback but globalLangfuseLogger is not provided and dynamic params are not passed" - ) - - -def _dynamic_langfuse_credentials_are_passed( - standard_callback_dynamic_params: StandardCallbackDynamicParams, -) -> bool: - """ - This function is used to check if the dynamic langfuse credentials are passed in standard_callback_dynamic_params - - Returns: - bool: True if the dynamic langfuse credentials are passed, False otherwise - """ - if ( - standard_callback_dynamic_params.get("langfuse_host") is not None - or standard_callback_dynamic_params.get("langfuse_public_key") is not None - or standard_callback_dynamic_params.get("langfuse_secret") is not None - or standard_callback_dynamic_params.get("langfuse_secret_key") is not None - ): - return True - return False diff --git a/litellm/integrations/langfuse/langfuse_handler.py b/litellm/integrations/langfuse/langfuse_handler.py new file mode 100644 index 0000000000..e3ce736b54 --- /dev/null +++ b/litellm/integrations/langfuse/langfuse_handler.py @@ -0,0 +1,168 @@ +""" +This file contains the LangFuseHandler class + +Used to get the LangFuseLogger for a given request + +Handles Key/Team Based Langfuse Logging +""" + +from typing import TYPE_CHECKING, Any, Dict, Optional + +from litellm.litellm_core_utils.litellm_logging import StandardCallbackDynamicParams + +from .langfuse import LangFuseLogger, LangfuseLoggingConfig + +if TYPE_CHECKING: + from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache +else: + DynamicLoggingCache = Any + + +class LangFuseHandler: + + @staticmethod + def get_langfuse_logger_for_request( + standard_callback_dynamic_params: StandardCallbackDynamicParams, + in_memory_dynamic_logger_cache: DynamicLoggingCache, + globalLangfuseLogger: Optional[LangFuseLogger] = None, + ) -> LangFuseLogger: + """ + This function is used to get the LangFuseLogger for a given request + + 1. If dynamic credentials are passed + - check if a LangFuseLogger is cached for the dynamic credentials + - if cached LangFuseLogger is not found, create a new LangFuseLogger and cache it + + 2. If dynamic credentials are not passed return the globalLangfuseLogger + + """ + temp_langfuse_logger: Optional[LangFuseLogger] = globalLangfuseLogger + if ( + LangFuseHandler._dynamic_langfuse_credentials_are_passed( + standard_callback_dynamic_params + ) + is False + ): + return LangFuseHandler._return_global_langfuse_logger( + globalLangfuseLogger=globalLangfuseLogger, + in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, + ) + + # get langfuse logging config to use for this request, based on standard_callback_dynamic_params + _credentials = LangFuseHandler.get_dynamic_langfuse_logging_config( + globalLangfuseLogger=globalLangfuseLogger, + standard_callback_dynamic_params=standard_callback_dynamic_params, + ) + credentials_dict = dict(_credentials) + + # check if langfuse logger is already cached + temp_langfuse_logger = in_memory_dynamic_logger_cache.get_cache( + credentials=credentials_dict, service_name="langfuse" + ) + + # if not cached, create a new langfuse logger and cache it + if temp_langfuse_logger is None: + temp_langfuse_logger = ( + LangFuseHandler._create_langfuse_logger_from_credentials( + credentials=credentials_dict, + in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, + ) + ) + + return temp_langfuse_logger + + @staticmethod + def _return_global_langfuse_logger( + globalLangfuseLogger: Optional[LangFuseLogger], + in_memory_dynamic_logger_cache: DynamicLoggingCache, + ) -> LangFuseLogger: + """ + Returns the Global LangfuseLogger set on litellm + + (this is the default langfuse logger - used when no dynamic credentials are passed) + + If no Global LangfuseLogger is set, it will check in_memory_dynamic_logger_cache for a cached LangFuseLogger + This function is used to return the globalLangfuseLogger if it exists, otherwise it will check in_memory_dynamic_logger_cache for a cached LangFuseLogger + """ + if globalLangfuseLogger is not None: + return globalLangfuseLogger + + credentials_dict: Dict[str, Any] = ( + {} + ) # the global langfuse logger uses Environment Variables, there are no dynamic credentials + globalLangfuseLogger = in_memory_dynamic_logger_cache.get_cache( + credentials=credentials_dict, + service_name="langfuse", + ) + if globalLangfuseLogger is None: + globalLangfuseLogger = ( + LangFuseHandler._create_langfuse_logger_from_credentials( + credentials=credentials_dict, + in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, + ) + ) + return globalLangfuseLogger + + @staticmethod + def _create_langfuse_logger_from_credentials( + credentials: Dict, + in_memory_dynamic_logger_cache: DynamicLoggingCache, + ) -> LangFuseLogger: + """ + This function is used to + 1. create a LangFuseLogger from the credentials + 2. cache the LangFuseLogger to prevent re-creating it for the same credentials + """ + + langfuse_logger = LangFuseLogger( + langfuse_public_key=credentials.get("langfuse_public_key"), + langfuse_secret=credentials.get("langfuse_secret"), + langfuse_host=credentials.get("langfuse_host"), + ) + in_memory_dynamic_logger_cache.set_cache( + credentials=credentials, + service_name="langfuse", + logging_obj=langfuse_logger, + ) + return langfuse_logger + + @staticmethod + def get_dynamic_langfuse_logging_config( + standard_callback_dynamic_params: StandardCallbackDynamicParams, + globalLangfuseLogger: Optional[LangFuseLogger] = None, + ) -> LangfuseLoggingConfig: + """ + This function is used to get the Langfuse logging config to use for a given request. + + It checks if the dynamic parameters are provided in the standard_callback_dynamic_params and uses them to get the Langfuse logging config. + + If no dynamic parameters are provided, it uses the `globalLangfuseLogger` values + """ + # only use dynamic params if langfuse credentials are passed dynamically + return LangfuseLoggingConfig( + langfuse_secret=standard_callback_dynamic_params.get("langfuse_secret") + or standard_callback_dynamic_params.get("langfuse_secret_key"), + langfuse_public_key=standard_callback_dynamic_params.get( + "langfuse_public_key" + ), + langfuse_host=standard_callback_dynamic_params.get("langfuse_host"), + ) + + @staticmethod + def _dynamic_langfuse_credentials_are_passed( + standard_callback_dynamic_params: StandardCallbackDynamicParams, + ) -> bool: + """ + This function is used to check if the dynamic langfuse credentials are passed in standard_callback_dynamic_params + + Returns: + bool: True if the dynamic langfuse credentials are passed, False otherwise + """ + if ( + standard_callback_dynamic_params.get("langfuse_host") is not None + or standard_callback_dynamic_params.get("langfuse_public_key") is not None + or standard_callback_dynamic_params.get("langfuse_secret") is not None + or standard_callback_dynamic_params.get("langfuse_secret_key") is not None + ): + return True + return False diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 7aee381516..8a80f9b963 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -70,7 +70,8 @@ from ..integrations.gcs_bucket.gcs_bucket import GCSBucketLogger from ..integrations.greenscale import GreenscaleLogger from ..integrations.helicone import HeliconeLogger from ..integrations.lago import LagoLogger -from ..integrations.langfuse import LangFuseLogger +from ..integrations.langfuse.langfuse import LangFuseLogger +from ..integrations.langfuse.langfuse_handler import LangFuseHandler from ..integrations.langsmith import LangsmithLogger from ..integrations.litedebugger import LiteDebugger from ..integrations.literal_ai import LiteralAILogger @@ -1116,74 +1117,13 @@ class Logging: print_verbose("reaches langfuse for streaming logging!") result = kwargs["complete_streaming_response"] - temp_langfuse_logger = langFuseLogger - if langFuseLogger is None or ( - ( - self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - != langFuseLogger.public_key - ) - or ( - self.standard_callback_dynamic_params.get( - "langfuse_secret" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_secret" - ) - != langFuseLogger.secret_key - ) - or ( - self.standard_callback_dynamic_params.get( - "langfuse_host" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_host" - ) - != langFuseLogger.langfuse_host - ) - ): - credentials = { - "langfuse_public_key": self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ), - "langfuse_secret": self.standard_callback_dynamic_params.get( - "langfuse_secret" - ), - "langfuse_host": self.standard_callback_dynamic_params.get( - "langfuse_host" - ), - } - temp_langfuse_logger = ( - in_memory_dynamic_logger_cache.get_cache( - credentials=credentials, service_name="langfuse" - ) - ) - if temp_langfuse_logger is None: - temp_langfuse_logger = LangFuseLogger( - langfuse_public_key=self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ), - langfuse_secret=self.standard_callback_dynamic_params.get( - "langfuse_secret" - ), - langfuse_host=self.standard_callback_dynamic_params.get( - "langfuse_host" - ), - ) - in_memory_dynamic_logger_cache.set_cache( - credentials=credentials, - service_name="langfuse", - logging_obj=temp_langfuse_logger, - ) - if temp_langfuse_logger is not None: - _response = temp_langfuse_logger.log_event( + langfuse_logger_to_use = LangFuseHandler.get_langfuse_logger_for_request( + globalLangfuseLogger=langFuseLogger, + standard_callback_dynamic_params=self.standard_callback_dynamic_params, + in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, + ) + if langfuse_logger_to_use is not None: + _response = langfuse_logger_to_use.log_event( kwargs=kwargs, response_obj=result, start_time=start_time, @@ -1909,50 +1849,12 @@ class Logging: ): # copy.deepcopy raises errors as this could be a coroutine kwargs[k] = v # this only logs streaming once, complete_streaming_response exists i.e when stream ends - if langFuseLogger is None or ( - ( - self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - != langFuseLogger.public_key - ) - or ( - self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ) - != langFuseLogger.public_key - ) - or ( - self.standard_callback_dynamic_params.get( - "langfuse_host" - ) - is not None - and self.standard_callback_dynamic_params.get( - "langfuse_host" - ) - != langFuseLogger.langfuse_host - ) - ): - langFuseLogger = LangFuseLogger( - langfuse_public_key=self.standard_callback_dynamic_params.get( - "langfuse_public_key" - ), - langfuse_secret=self.standard_callback_dynamic_params.get( - "langfuse_secret" - ), - langfuse_host=self.standard_callback_dynamic_params.get( - "langfuse_host" - ), - ) - _response = langFuseLogger.log_event( + langfuse_logger_to_use = LangFuseHandler.get_langfuse_logger_for_request( + globalLangfuseLogger=langFuseLogger, + standard_callback_dynamic_params=self.standard_callback_dynamic_params, + in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, + ) + _response = langfuse_logger_to_use.log_event( start_time=start_time, end_time=end_time, response_obj=None, diff --git a/litellm/proxy/health_endpoints/_health_endpoints.py b/litellm/proxy/health_endpoints/_health_endpoints.py index 1f2eb5d6d6..e12e836de1 100644 --- a/litellm/proxy/health_endpoints/_health_endpoints.py +++ b/litellm/proxy/health_endpoints/_health_endpoints.py @@ -120,7 +120,7 @@ async def health_services_endpoint( # noqa: PLR0915 } if service == "langfuse": - from litellm.integrations.langfuse import LangFuseLogger + from litellm.integrations.langfuse.langfuse import LangFuseLogger langfuse_logger = LangFuseLogger() langfuse_logger.Langfuse.auth_check() diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index bae738c735..8f22d0d780 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -1,48 +1,20 @@ model_list: - ################################################################################ - # Azure - - model_name: gpt-4o-mini - litellm_params: - model: azure/gpt-4o-mini - api_base: https://amazin-prod.openai.azure.com - api_key: "os.environ/AZURE_GPT_4O" - deployment_id: gpt-4o-mini - model_name: gpt-4o litellm_params: - model: azure/gpt-4o - api_base: https://very-cool-prod.openai.azure.com - api_key: "os.environ/AZURE_GPT_4O" - deployment_id: gpt-4o + model: gpt-4o + api_key: os.environ/OPENAI_API_KEY + tpm: 1000000 + rpm: 10000 + - ################################################################################ - # Fireworks - - model_name: fireworks-llama-v3p1-405b-instruct - litellm_params: - model: fireworks_ai/accounts/fireworks/models/llama-v3p1-405b-instruct - api_key: "os.environ/FIREWORKS" - - model_name: fireworks-llama-v3p1-70b-instruct - litellm_params: - model: fireworks_ai/accounts/fireworks/models/llama-v3p1-70b-instruct - api_key: "os.environ/FIREWORKS" - general_settings: - alerting_threshold: 300 # sends alerts if requests hang for 5min+ and responses take 5min+ -litellm_settings: # module level litellm settings - https://github.com/BerriAI/litellm/blob/main/litellm/__init__.py - success_callback: ["prometheus"] - service_callback: ["prometheus_system"] - drop_params: False # Raise an exception if the openai param being passed in isn't supported. - cache: false - default_internal_user_params: - user_role: os.environ/DEFAULT_USER_ROLE + # master key is set via env var + # master_key: ####### + proxy_batch_write_at: 60 # Batch write spend updates every 60s - success_callback: ["s3"] - s3_callback_params: - s3_bucket_name: logs-bucket-litellm # AWS Bucket Name for S3 - s3_region_name: us-west-2 # AWS Region Name for S3 - s3_aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID # us os.environ/ to pass environment variables. This is AWS Access Key ID for S3 - s3_aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY # AWS Secret Access Key for S3 - s3_path: my-test-path # [OPTIONAL] set path in bucket you want to write logs to - s3_endpoint_url: https://s3.amazonaws.com # [OPTIONAL] S3 endpoint URL, if you want to use Backblaze/cloudflare s3 buckets +litellm_settings: + success_callback: ["langfuse"] -router_settings: - routing_strategy: simple-shuffle # "simple-shuffle" shown to result in highest throughput. https://docs.litellm.ai/docs/proxy/configs#load-balancing + # https://docs.litellm.ai/docs/proxy/reliability#default-fallbacks + default_fallbacks: ["gpt-4o-2024-08-06", "claude-3-5-sonnet-20240620"] + fallbacks: [{"gpt-4o-2024-08-06": ["claude-3-5-sonnet-20240620"]}, {"gpt-4o-2024-05-13": ["claude-3-5-sonnet-20240620"]}] \ No newline at end of file diff --git a/tests/local_testing/test_alangfuse.py b/tests/local_testing/test_alangfuse.py index e4c1393785..f04366b286 100644 --- a/tests/local_testing/test_alangfuse.py +++ b/tests/local_testing/test_alangfuse.py @@ -428,7 +428,7 @@ async def test_aaalangfuse_logging_metadata(langfuse_client): await asyncio.sleep(2) langfuse_client.flush() - # await asyncio.sleep(10) + await asyncio.sleep(4) # Tests the metadata filtering and the override of the output to be the last generation for trace_id, generation_ids in trace_identifiers.items(): @@ -625,7 +625,7 @@ def test_aaalangfuse_existing_trace_id(): import datetime import litellm - from litellm.integrations.langfuse import LangFuseLogger + from litellm.integrations.langfuse.langfuse import LangFuseLogger langfuse_Logger = LangFuseLogger( langfuse_public_key=os.getenv("LANGFUSE_PROJECT2_PUBLIC"), @@ -1125,7 +1125,7 @@ generation_params = { ) def test_langfuse_prompt_type(prompt): - from litellm.integrations.langfuse import _add_prompt_to_generation_params + from litellm.integrations.langfuse.langfuse import _add_prompt_to_generation_params clean_metadata = { "prompt": { @@ -1232,7 +1232,7 @@ def test_langfuse_prompt_type(prompt): def test_langfuse_logging_metadata(): - from litellm.integrations.langfuse import log_requester_metadata + from litellm.integrations.langfuse.langfuse import log_requester_metadata metadata = {"key": "value", "requester_metadata": {"key": "value"}} diff --git a/tests/logging_callback_tests/test_langfuse_unit_tests.py b/tests/logging_callback_tests/test_langfuse_unit_tests.py new file mode 100644 index 0000000000..2a6cbe00a9 --- /dev/null +++ b/tests/logging_callback_tests/test_langfuse_unit_tests.py @@ -0,0 +1,219 @@ +import json +import os +import sys +from datetime import datetime + +from pydantic.main import Model + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system-path + +import pytest +import litellm +import asyncio +import logging +from litellm._logging import verbose_logger +from litellm.integrations.langfuse.langfuse import ( + LangFuseLogger, +) +from litellm.integrations.langfuse.langfuse_handler import LangFuseHandler +from litellm.types.utils import StandardCallbackDynamicParams +from litellm.litellm_core_utils.litellm_logging import DynamicLoggingCache +from unittest.mock import Mock, patch + + +@pytest.fixture +def dynamic_logging_cache(): + return DynamicLoggingCache() + + +global_langfuse_logger = LangFuseLogger( + langfuse_public_key="global_public_key", + langfuse_secret="global_secret", + langfuse_host="https://global.langfuse.com", +) + + +# IMPORTANT: Test that passing both langfuse_secret_key and langfuse_secret works +standard_params_1 = StandardCallbackDynamicParams( + langfuse_public_key="test_public_key", + langfuse_secret="test_secret", + langfuse_host="https://test.langfuse.com", +) + +standard_params_2 = StandardCallbackDynamicParams( + langfuse_public_key="test_public_key", + langfuse_secret_key="test_secret", + langfuse_host="https://test.langfuse.com", +) + + +@pytest.mark.parametrize("globalLangfuseLogger", [None, global_langfuse_logger]) +@pytest.mark.parametrize("standard_params", [standard_params_1, standard_params_2]) +def test_get_langfuse_logger_for_request_with_dynamic_params( + dynamic_logging_cache, globalLangfuseLogger, standard_params +): + """ + If StandardCallbackDynamicParams contain langfuse credentials the returned Langfuse logger should use the dynamic params + + the new Langfuse logger should be cached + + Even if globalLangfuseLogger is provided, it should use dynamic params if they are passed + """ + + result = LangFuseHandler.get_langfuse_logger_for_request( + standard_callback_dynamic_params=standard_params, + in_memory_dynamic_logger_cache=dynamic_logging_cache, + globalLangfuseLogger=globalLangfuseLogger, + ) + + assert isinstance(result, LangFuseLogger) + assert result.public_key == "test_public_key" + assert result.secret_key == "test_secret" + assert result.langfuse_host == "https://test.langfuse.com" + + print("langfuse logger=", result) + print("vars in langfuse logger=", vars(result)) + + # Check if the logger is cached + cached_logger = dynamic_logging_cache.get_cache( + credentials={ + "langfuse_public_key": "test_public_key", + "langfuse_secret": "test_secret", + "langfuse_host": "https://test.langfuse.com", + }, + service_name="langfuse", + ) + assert cached_logger is result + + +@pytest.mark.parametrize("globalLangfuseLogger", [None, global_langfuse_logger]) +def test_get_langfuse_logger_for_request_with_no_dynamic_params( + dynamic_logging_cache, globalLangfuseLogger +): + """ + If StandardCallbackDynamicParams are not provided, the globalLangfuseLogger should be returned + """ + result = LangFuseHandler.get_langfuse_logger_for_request( + standard_callback_dynamic_params=StandardCallbackDynamicParams(), + in_memory_dynamic_logger_cache=dynamic_logging_cache, + globalLangfuseLogger=globalLangfuseLogger, + ) + + assert result is not None + assert isinstance(result, LangFuseLogger) + + print("langfuse logger=", result) + + if globalLangfuseLogger is not None: + assert result.public_key == "global_public_key" + assert result.secret_key == "global_secret" + assert result.langfuse_host == "https://global.langfuse.com" + + +def test_dynamic_langfuse_credentials_are_passed(): + # Test when credentials are passed + params_with_credentials = StandardCallbackDynamicParams( + langfuse_public_key="test_key", + langfuse_secret="test_secret", + langfuse_host="https://test.langfuse.com", + ) + assert ( + LangFuseHandler._dynamic_langfuse_credentials_are_passed( + params_with_credentials + ) + is True + ) + + # Test when no credentials are passed + params_without_credentials = StandardCallbackDynamicParams() + assert ( + LangFuseHandler._dynamic_langfuse_credentials_are_passed( + params_without_credentials + ) + is False + ) + + # Test when only some credentials are passed + params_partial_credentials = StandardCallbackDynamicParams( + langfuse_public_key="test_key" + ) + assert ( + LangFuseHandler._dynamic_langfuse_credentials_are_passed( + params_partial_credentials + ) + is True + ) + + +def test_get_dynamic_langfuse_logging_config(): + # Test with dynamic params + dynamic_params = StandardCallbackDynamicParams( + langfuse_public_key="dynamic_key", + langfuse_secret="dynamic_secret", + langfuse_host="https://dynamic.langfuse.com", + ) + config = LangFuseHandler.get_dynamic_langfuse_logging_config(dynamic_params) + assert config["langfuse_public_key"] == "dynamic_key" + assert config["langfuse_secret"] == "dynamic_secret" + assert config["langfuse_host"] == "https://dynamic.langfuse.com" + + # Test with no dynamic params + empty_params = StandardCallbackDynamicParams() + config = LangFuseHandler.get_dynamic_langfuse_logging_config(empty_params) + assert config["langfuse_public_key"] is None + assert config["langfuse_secret"] is None + assert config["langfuse_host"] is None + + +def test_return_global_langfuse_logger(): + mock_cache = Mock() + global_logger = LangFuseLogger( + langfuse_public_key="global_key", langfuse_secret="global_secret" + ) + + # Test with existing global logger + result = LangFuseHandler._return_global_langfuse_logger(global_logger, mock_cache) + assert result == global_logger + + # Test without global logger, but with cached logger, should return cached logger + mock_cache.get_cache.return_value = global_logger + result = LangFuseHandler._return_global_langfuse_logger(None, mock_cache) + assert result == global_logger + + # Test without global logger and without cached logger, should create new logger + mock_cache.get_cache.return_value = None + with patch.object( + LangFuseHandler, + "_create_langfuse_logger_from_credentials", + return_value=global_logger, + ): + result = LangFuseHandler._return_global_langfuse_logger(None, mock_cache) + assert result == global_logger + + +def test_get_langfuse_logger_for_request_with_cached_logger(): + """ + Test that get_langfuse_logger_for_request returns the cached logger if it exists when dynamic params are passed + """ + mock_cache = Mock() + cached_logger = LangFuseLogger( + langfuse_public_key="cached_key", langfuse_secret="cached_secret" + ) + mock_cache.get_cache.return_value = cached_logger + + dynamic_params = StandardCallbackDynamicParams( + langfuse_public_key="test_key", + langfuse_secret="test_secret", + langfuse_host="https://test.langfuse.com", + ) + + result = LangFuseHandler.get_langfuse_logger_for_request( + standard_callback_dynamic_params=dynamic_params, + in_memory_dynamic_logger_cache=mock_cache, + globalLangfuseLogger=None, + ) + + assert result == cached_logger + mock_cache.get_cache.assert_called_once() From d063086bbf1f2fa5e40e5577a5b5ee1af631ef6c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 20:31:57 +0530 Subject: [PATCH 21/62] Revert "(refactor) litellm.Router client initialization utils (#6394)" (#6403) This reverts commit b70147f63b5ad95d90be371a50c6248fe21b20e8. --- litellm/router.py | 23 +- .../client_initalization_utils.py | 1055 ++++++++--------- tests/local_testing/test_router_init.py | 284 ----- 3 files changed, 478 insertions(+), 884 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 0cad565b00..142a781bbe 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -63,7 +63,10 @@ from litellm.router_utils.batch_utils import ( _get_router_metadata_variable_name, replace_model_in_jsonl, ) -from litellm.router_utils.client_initalization_utils import InitalizeOpenAISDKClient +from litellm.router_utils.client_initalization_utils import ( + set_client, + should_initialize_sync_client, +) from litellm.router_utils.cooldown_cache import CooldownCache from litellm.router_utils.cooldown_callbacks import router_cooldown_event_callback from litellm.router_utils.cooldown_handlers import ( @@ -3948,7 +3951,7 @@ class Router: raise Exception(f"Unsupported provider - {custom_llm_provider}") # init OpenAI, Azure clients - InitalizeOpenAISDKClient.set_client( + set_client( litellm_router_instance=self, model=deployment.to_json(exclude_none=True) ) @@ -4658,9 +4661,7 @@ class Router: """ Re-initialize the client """ - InitalizeOpenAISDKClient.set_client( - litellm_router_instance=self, model=deployment - ) + set_client(litellm_router_instance=self, model=deployment) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4670,9 +4671,7 @@ class Router: """ Re-initialize the client """ - InitalizeOpenAISDKClient.set_client( - litellm_router_instance=self, model=deployment - ) + set_client(litellm_router_instance=self, model=deployment) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4683,9 +4682,7 @@ class Router: """ Re-initialize the client """ - InitalizeOpenAISDKClient.set_client( - litellm_router_instance=self, model=deployment - ) + set_client(litellm_router_instance=self, model=deployment) client = self.cache.get_cache(key=cache_key) return client else: @@ -4695,9 +4692,7 @@ class Router: """ Re-initialize the client """ - InitalizeOpenAISDKClient.set_client( - litellm_router_instance=self, model=deployment - ) + set_client(litellm_router_instance=self, model=deployment) client = self.cache.get_cache(key=cache_key) return client diff --git a/litellm/router_utils/client_initalization_utils.py b/litellm/router_utils/client_initalization_utils.py index 679cefadfa..6c845296a8 100644 --- a/litellm/router_utils/client_initalization_utils.py +++ b/litellm/router_utils/client_initalization_utils.py @@ -1,11 +1,10 @@ import asyncio import os import traceback -from typing import TYPE_CHECKING, Any, Callable, Optional, Union +from typing import TYPE_CHECKING, Any, Callable, Optional import httpx import openai -from pydantic import BaseModel import litellm from litellm import get_secret, get_secret_str @@ -17,511 +16,89 @@ from litellm.secret_managers.get_azure_ad_token_provider import ( from litellm.utils import calculate_max_parallel_requests if TYPE_CHECKING: - from httpx import Timeout as httpxTimeout - from litellm.router import Router as _Router LitellmRouter = _Router else: LitellmRouter = Any - httpxTimeout = Any -class OpenAISDKClientInitializationParams(BaseModel): - api_key: Optional[str] - api_base: Optional[str] - api_version: Optional[str] - azure_ad_token_provider: Optional[Callable[[], str]] - timeout: Optional[Union[float, httpxTimeout]] - stream_timeout: Optional[Union[float, httpxTimeout]] - max_retries: int - organization: Optional[str] - - # Internal LiteLLM specific params - custom_llm_provider: Optional[str] - model_name: str - - -class InitalizeOpenAISDKClient: +def should_initialize_sync_client( + litellm_router_instance: LitellmRouter, +) -> bool: """ - OpenAI Python SDK requires creating a OpenAI/AzureOpenAI client - this class is responsible for creating that client + Returns if Sync OpenAI, Azure Clients should be initialized. + + Do not init sync clients when router.router_general_settings.async_only_mode is True + """ + if litellm_router_instance is None: + return False - @staticmethod - def should_initialize_sync_client( - litellm_router_instance: LitellmRouter, - ) -> bool: - """ - Returns if Sync OpenAI, Azure Clients should be initialized. - - Do not init sync clients when router.router_general_settings.async_only_mode is True - - """ - if litellm_router_instance is None: + if litellm_router_instance.router_general_settings is not None: + if ( + hasattr(litellm_router_instance, "router_general_settings") + and hasattr( + litellm_router_instance.router_general_settings, "async_only_mode" + ) + and litellm_router_instance.router_general_settings.async_only_mode is True + ): return False - if litellm_router_instance.router_general_settings is not None: - if ( - hasattr(litellm_router_instance, "router_general_settings") - and hasattr( - litellm_router_instance.router_general_settings, "async_only_mode" - ) - and litellm_router_instance.router_general_settings.async_only_mode - is True - ): - return False + return True - return True - @staticmethod - def set_client( # noqa: PLR0915 - litellm_router_instance: LitellmRouter, model: dict - ): - """ - - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 - - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 - """ - client_ttl = litellm_router_instance.client_ttl - litellm_params = model.get("litellm_params", {}) - model_name = litellm_params.get("model") - model_id = model["model_info"]["id"] - # ### IF RPM SET - initialize a semaphore ### - rpm = litellm_params.get("rpm", None) - tpm = litellm_params.get("tpm", None) - max_parallel_requests = litellm_params.get("max_parallel_requests", None) - calculated_max_parallel_requests = calculate_max_parallel_requests( - rpm=rpm, - max_parallel_requests=max_parallel_requests, - tpm=tpm, - default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, +def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PLR0915 + """ + - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 + - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 + """ + client_ttl = litellm_router_instance.client_ttl + litellm_params = model.get("litellm_params", {}) + model_name = litellm_params.get("model") + model_id = model["model_info"]["id"] + # ### IF RPM SET - initialize a semaphore ### + rpm = litellm_params.get("rpm", None) + tpm = litellm_params.get("tpm", None) + max_parallel_requests = litellm_params.get("max_parallel_requests", None) + calculated_max_parallel_requests = calculate_max_parallel_requests( + rpm=rpm, + max_parallel_requests=max_parallel_requests, + tpm=tpm, + default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, + ) + if calculated_max_parallel_requests: + semaphore = asyncio.Semaphore(calculated_max_parallel_requests) + cache_key = f"{model_id}_max_parallel_requests_client" + litellm_router_instance.cache.set_cache( + key=cache_key, + value=semaphore, + local_only=True, ) - if calculated_max_parallel_requests: - semaphore = asyncio.Semaphore(calculated_max_parallel_requests) - cache_key = f"{model_id}_max_parallel_requests_client" - litellm_router_instance.cache.set_cache( - key=cache_key, - value=semaphore, - local_only=True, - ) - #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## - custom_llm_provider = litellm_params.get("custom_llm_provider") - custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" - default_api_base = None - default_api_key = None - if custom_llm_provider in litellm.openai_compatible_providers: - _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( - model=model_name - ) - default_api_base = api_base - default_api_key = api_key - - if InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( - model_name=model_name, - custom_llm_provider=custom_llm_provider, - ): - client_initialization_params = ( - InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - ) - - ############### Unpack client initialization params ####################### - api_key = client_initialization_params.api_key - api_base = client_initialization_params.api_base - api_version: Optional[str] = client_initialization_params.api_version - timeout: Optional[Union[float, httpxTimeout]] = ( - client_initialization_params.timeout - ) - stream_timeout: Optional[Union[float, httpxTimeout]] = ( - client_initialization_params.stream_timeout - ) - max_retries: int = client_initialization_params.max_retries - organization: Optional[str] = client_initialization_params.organization - azure_ad_token_provider: Optional[Callable[[], str]] = ( - client_initialization_params.azure_ad_token_provider - ) - custom_llm_provider = client_initialization_params.custom_llm_provider - model_name = client_initialization_params.model_name - ########################################################################## - - if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": - if api_base is None or not isinstance(api_base, str): - filtered_litellm_params = { - k: v - for k, v in model["litellm_params"].items() - if k != "api_key" - } - _filtered_model = { - "model_name": model["model_name"], - "litellm_params": filtered_litellm_params, - } - raise ValueError( - f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" - ) - azure_ad_token = litellm_params.get("azure_ad_token") - if azure_ad_token is not None: - if azure_ad_token.startswith("oidc/"): - azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) - elif ( - azure_ad_token_provider is None - and litellm.enable_azure_ad_token_refresh is True - ): - try: - azure_ad_token_provider = get_azure_ad_token_provider() - except ValueError: - verbose_router_logger.debug( - "Azure AD Token Provider could not be used." - ) - if api_version is None: - api_version = os.getenv( - "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION - ) - - if "gateway.ai.cloudflare.com" in api_base: - if not api_base.endswith("/"): - api_base += "/" - azure_model = model_name.replace("azure/", "") - api_base += f"{azure_model}" - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=timeout, - max_retries=max_retries, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=timeout, - max_retries=max_retries, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - # streaming clients can have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=stream_timeout, - max_retries=max_retries, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=stream_timeout, - max_retries=max_retries, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - else: - _api_key = api_key - if _api_key is not None and isinstance(_api_key, str): - # only show first 5 chars of api_key - _api_key = _api_key[:8] + "*" * 15 - verbose_router_logger.debug( - f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" - ) - azure_client_params = { - "api_key": api_key, - "azure_endpoint": api_base, - "api_version": api_version, - "azure_ad_token": azure_ad_token, - "azure_ad_token_provider": azure_ad_token_provider, - } - - if azure_ad_token_provider is not None: - azure_client_params["azure_ad_token_provider"] = ( - azure_ad_token_provider - ) - from litellm.llms.AzureOpenAI.azure import ( - select_azure_base_url_or_endpoint, - ) - - # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client - # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client - azure_client_params = select_azure_base_url_or_endpoint( - azure_client_params - ) - - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, - timeout=timeout, - max_retries=max_retries, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, - timeout=timeout, - max_retries=max_retries, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, - timeout=stream_timeout, - max_retries=max_retries, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, - timeout=stream_timeout, - max_retries=max_retries, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - else: - _api_key = api_key # type: ignore - if _api_key is not None and isinstance(_api_key, str): - # only show first 5 chars of api_key - _api_key = _api_key[:8] + "*" * 15 - verbose_router_logger.debug( - f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" - ) - cache_key = f"{model_id}_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, - max_retries=max_retries, - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, - max_retries=max_retries, - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, - max_retries=max_retries, - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - if InitalizeOpenAISDKClient.should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, - max_retries=max_retries, - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - - @staticmethod - def _get_client_initialization_params( - model: dict, - model_name: str, - custom_llm_provider: Optional[str], - litellm_params: dict, - default_api_key: Optional[str], - default_api_base: Optional[str], - ) -> OpenAISDKClientInitializationParams: - """ - Returns params to use for initializing OpenAI SDK clients (for OpenAI, Azure OpenAI, OpenAI Compatible Providers) - - Args: - model: model dict - model_name: model name - custom_llm_provider: custom llm provider - litellm_params: litellm params - default_api_key: default api key - default_api_base: default api base - - Returns: - OpenAISDKClientInitializationParams - """ + #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## + custom_llm_provider = litellm_params.get("custom_llm_provider") + custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" + default_api_base = None + default_api_key = None + if custom_llm_provider in litellm.openai_compatible_providers: + _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( + model=model_name + ) + default_api_base = api_base + default_api_key = api_key + if ( + model_name in litellm.open_ai_chat_completion_models + or custom_llm_provider in litellm.openai_compatible_providers + or custom_llm_provider == "azure" + or custom_llm_provider == "azure_text" + or custom_llm_provider == "custom_openai" + or custom_llm_provider == "openai" + or custom_llm_provider == "text-completion-openai" + or "ft:gpt-3.5-turbo" in model_name + or model_name in litellm.open_ai_embedding_models + ): is_azure_ai_studio_model: bool = False if custom_llm_provider == "azure": if litellm.utils._is_non_openai_azure_model(model_name): @@ -534,7 +111,8 @@ class InitalizeOpenAISDKClient: # we do this here because we init clients for Azure, OpenAI and we need to set the right key api_key = litellm_params.get("api_key") or default_api_key if api_key and isinstance(api_key, str) and api_key.startswith("os.environ/"): - api_key = get_secret_str(api_key) + api_key_env_name = api_key.replace("os.environ/", "") + api_key = get_secret_str(api_key_env_name) litellm_params["api_key"] = api_key api_base = litellm_params.get("api_base") @@ -543,7 +121,8 @@ class InitalizeOpenAISDKClient: api_base or base_url or default_api_base ) # allow users to pass in `api_base` or `base_url` for azure if api_base and api_base.startswith("os.environ/"): - api_base = get_secret_str(api_base) + api_base_env_name = api_base.replace("os.environ/", "") + api_base = get_secret_str(api_base_env_name) litellm_params["api_base"] = api_base ## AZURE AI STUDIO MISTRAL CHECK ## @@ -568,132 +147,436 @@ class InitalizeOpenAISDKClient: api_version = litellm_params.get("api_version") if api_version and api_version.startswith("os.environ/"): - api_version = get_secret_str(api_version) + api_version_env_name = api_version.replace("os.environ/", "") + api_version = get_secret_str(api_version_env_name) litellm_params["api_version"] = api_version - timeout: Optional[Union[float, str, httpxTimeout]] = ( + timeout: Optional[float] = ( litellm_params.pop("timeout", None) or litellm.request_timeout ) if isinstance(timeout, str) and timeout.startswith("os.environ/"): - timeout = float(get_secret(timeout)) # type: ignore + timeout_env_name = timeout.replace("os.environ/", "") + timeout = get_secret(timeout_env_name) # type: ignore litellm_params["timeout"] = timeout - stream_timeout: Optional[Union[float, str, httpxTimeout]] = litellm_params.pop( + stream_timeout: Optional[float] = litellm_params.pop( "stream_timeout", timeout ) # if no stream_timeout is set, default to timeout if isinstance(stream_timeout, str) and stream_timeout.startswith("os.environ/"): - stream_timeout = float(get_secret(stream_timeout)) # type: ignore + stream_timeout_env_name = stream_timeout.replace("os.environ/", "") + stream_timeout = get_secret(stream_timeout_env_name) # type: ignore litellm_params["stream_timeout"] = stream_timeout max_retries: Optional[int] = litellm_params.pop( "max_retries", 0 ) # router handles retry logic if isinstance(max_retries, str) and max_retries.startswith("os.environ/"): - max_retries = get_secret(max_retries) # type: ignore + max_retries_env_name = max_retries.replace("os.environ/", "") + max_retries = get_secret(max_retries_env_name) # type: ignore litellm_params["max_retries"] = max_retries organization = litellm_params.get("organization", None) if isinstance(organization, str) and organization.startswith("os.environ/"): - organization = get_secret_str(organization) + organization_env_name = organization.replace("os.environ/", "") + organization = get_secret_str(organization_env_name) litellm_params["organization"] = organization azure_ad_token_provider: Optional[Callable[[], str]] = None - tenant_id = litellm_params.get("tenant_id") - if tenant_id is not None: + if litellm_params.get("tenant_id"): verbose_router_logger.debug("Using Azure AD Token Provider for Azure Auth") - azure_ad_token_provider = ( - InitalizeOpenAISDKClient.get_azure_ad_token_from_entrata_id( - tenant_id=tenant_id, - client_id=litellm_params.get("client_id"), - client_secret=litellm_params.get("client_secret"), + azure_ad_token_provider = get_azure_ad_token_from_entrata_id( + tenant_id=litellm_params.get("tenant_id"), + client_id=litellm_params.get("client_id"), + client_secret=litellm_params.get("client_secret"), + ) + + if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": + if api_base is None or not isinstance(api_base, str): + filtered_litellm_params = { + k: v for k, v in model["litellm_params"].items() if k != "api_key" + } + _filtered_model = { + "model_name": model["model_name"], + "litellm_params": filtered_litellm_params, + } + raise ValueError( + f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" ) + azure_ad_token = litellm_params.get("azure_ad_token") + if azure_ad_token is not None: + if azure_ad_token.startswith("oidc/"): + azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) + elif ( + azure_ad_token_provider is None + and litellm.enable_azure_ad_token_refresh is True + ): + try: + azure_ad_token_provider = get_azure_ad_token_provider() + except ValueError: + verbose_router_logger.debug( + "Azure AD Token Provider could not be used." + ) + if api_version is None: + api_version = os.getenv( + "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION + ) + + if "gateway.ai.cloudflare.com" in api_base: + if not api_base.endswith("/"): + api_base += "/" + azure_model = model_name.replace("azure/", "") + api_base += f"{azure_model}" + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + # streaming clients can have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + else: + _api_key = api_key + if _api_key is not None and isinstance(_api_key, str): + # only show first 5 chars of api_key + _api_key = _api_key[:8] + "*" * 15 + verbose_router_logger.debug( + f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" + ) + azure_client_params = { + "api_key": api_key, + "azure_endpoint": api_base, + "api_version": api_version, + "azure_ad_token": azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider, + } + + if azure_ad_token_provider is not None: + azure_client_params["azure_ad_token_provider"] = ( + azure_ad_token_provider + ) + from litellm.llms.AzureOpenAI.azure import ( + select_azure_base_url_or_endpoint, + ) + + # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client + # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client + azure_client_params = select_azure_base_url_or_endpoint( + azure_client_params + ) + + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + else: + _api_key = api_key # type: ignore + if _api_key is not None and isinstance(_api_key, str): + # only show first 5 chars of api_key + _api_key = _api_key[:8] + "*" * 15 + verbose_router_logger.debug( + f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" ) - - return OpenAISDKClientInitializationParams( - api_key=api_key, - api_base=api_base, - api_version=api_version, - azure_ad_token_provider=azure_ad_token_provider, - timeout=timeout, # type: ignore - stream_timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - custom_llm_provider=custom_llm_provider, - model_name=model_name, - ) - - @staticmethod - def _should_create_openai_sdk_client_for_model( - model_name: str, - custom_llm_provider: str, - ) -> bool: - """ - Returns True if a OpenAI SDK client should be created for a given model - - We need a OpenAI SDK client for models that are callsed using OpenAI Python SDK - Azure OpenAI, OpenAI, OpenAI Compatible Providers, OpenAI Embedding Models - """ - if ( - model_name in litellm.open_ai_chat_completion_models - or custom_llm_provider in litellm.openai_compatible_providers - or custom_llm_provider == "azure" - or custom_llm_provider == "azure_text" - or custom_llm_provider == "custom_openai" - or custom_llm_provider == "openai" - or custom_llm_provider == "text-completion-openai" - or "ft:gpt-3.5-turbo" in model_name - or model_name in litellm.open_ai_embedding_models - ): - return True - return False - - @staticmethod - def get_azure_ad_token_from_entrata_id( - tenant_id: str, client_id: Optional[str], client_secret: Optional[str] - ) -> Callable[[], str]: - from azure.identity import ( - ClientSecretCredential, - DefaultAzureCredential, - get_bearer_token_provider, - ) - - if client_id is None or client_secret is None: - raise ValueError( - "client_id and client_secret must be provided when using `tenant_id`" + cache_key = f"{model_id}_async_client" + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + organization=organization, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr - verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + organization=organization, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr - if tenant_id.startswith("os.environ/"): - _tenant_id = get_secret_str(tenant_id) - else: - _tenant_id = tenant_id + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + organization=organization, + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr - if client_id.startswith("os.environ/"): - _client_id = get_secret_str(client_id) - else: - _client_id = client_id + if should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_client" + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + organization=organization, + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr - if client_secret.startswith("os.environ/"): - _client_secret = get_secret_str(client_secret) - else: - _client_secret = client_secret - verbose_router_logger.debug( - "tenant_id %s, client_id %s, client_secret %s", - _tenant_id, - _client_id, - _client_secret, - ) - if _tenant_id is None or _client_id is None or _client_secret is None: - raise ValueError("tenant_id, client_id, and client_secret must be provided") - credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) +def get_azure_ad_token_from_entrata_id( + tenant_id: str, client_id: str, client_secret: str +) -> Callable[[], str]: + from azure.identity import ( + ClientSecretCredential, + DefaultAzureCredential, + get_bearer_token_provider, + ) - verbose_router_logger.debug("credential %s", credential) + verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") - token_provider = get_bearer_token_provider( - credential, "https://cognitiveservices.azure.com/.default" - ) + if tenant_id.startswith("os.environ/"): + _tenant_id = get_secret_str(tenant_id) + else: + _tenant_id = tenant_id - verbose_router_logger.debug("token_provider %s", token_provider) + if client_id.startswith("os.environ/"): + _client_id = get_secret_str(client_id) + else: + _client_id = client_id - return token_provider + if client_secret.startswith("os.environ/"): + _client_secret = get_secret_str(client_secret) + else: + _client_secret = client_secret + + verbose_router_logger.debug( + "tenant_id %s, client_id %s, client_secret %s", + _tenant_id, + _client_id, + _client_secret, + ) + if _tenant_id is None or _client_id is None or _client_secret is None: + raise ValueError("tenant_id, client_id, and client_secret must be provided") + credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) + + verbose_router_logger.debug("credential %s", credential) + + token_provider = get_bearer_token_provider( + credential, "https://cognitiveservices.azure.com/.default" + ) + + verbose_router_logger.debug("token_provider %s", token_provider) + + return token_provider diff --git a/tests/local_testing/test_router_init.py b/tests/local_testing/test_router_init.py index 45695f3c3d..3733af252b 100644 --- a/tests/local_testing/test_router_init.py +++ b/tests/local_testing/test_router_init.py @@ -17,10 +17,6 @@ from dotenv import load_dotenv import litellm from litellm import Router -from litellm.router_utils.client_initalization_utils import ( - InitalizeOpenAISDKClient, - OpenAISDKClientInitializationParams, -) load_dotenv() @@ -700,283 +696,3 @@ def test_init_router_with_supported_environments(environment, expected_models): assert set(_model_list) == set(expected_models) os.environ.pop("LITELLM_ENVIRONMENT") - - -def test_should_initialize_sync_client(): - from litellm.types.router import RouterGeneralSettings - - # Test case 1: Router instance is None - assert InitalizeOpenAISDKClient.should_initialize_sync_client(None) is False - - # Test case 2: Router instance without router_general_settings - router = Router(model_list=[]) - assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True - - # Test case 3: Router instance with async_only_mode = False - router = Router( - model_list=[], - router_general_settings=RouterGeneralSettings(async_only_mode=False), - ) - assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True - - # Test case 4: Router instance with async_only_mode = True - router = Router( - model_list=[], - router_general_settings=RouterGeneralSettings(async_only_mode=True), - ) - assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is False - - # Test case 5: Router instance with router_general_settings but without async_only_mode - router = Router(model_list=[], router_general_settings=RouterGeneralSettings()) - assert InitalizeOpenAISDKClient.should_initialize_sync_client(router) is True - - print("All test cases passed!") - - -@pytest.mark.parametrize( - "model_name, custom_llm_provider, expected_result", - [ - ("gpt-3.5-turbo", None, True), # OpenAI chat completion model - ("text-embedding-ada-002", None, True), # OpenAI embedding model - ("claude-2", None, False), # Non-OpenAI model - ("gpt-3.5-turbo", "azure", True), # Azure OpenAI - ("text-davinci-003", "azure_text", True), # Azure OpenAI - ("gpt-3.5-turbo", "openai", True), # OpenAI - ("custom-model", "custom_openai", True), # Custom OpenAI compatible - ("text-davinci-003", "text-completion-openai", True), # OpenAI text completion - ( - "ft:gpt-3.5-turbo-0613:my-org:custom-model:7p4lURel", - None, - True, - ), # Fine-tuned GPT model - ("mistral-7b", "huggingface", False), # Non-OpenAI provider - ("custom-model", "anthropic", False), # Non-OpenAI compatible provider - ], -) -def test_should_create_openai_sdk_client_for_model( - model_name, custom_llm_provider, expected_result -): - result = InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( - model_name, custom_llm_provider - ) - assert ( - result == expected_result - ), f"Failed for model: {model_name}, provider: {custom_llm_provider}" - - -def test_should_create_openai_sdk_client_for_model_openai_compatible_providers(): - # Test with a known OpenAI compatible provider - assert InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( - "custom-model", "groq" - ), "Should return True for OpenAI compatible provider" - - # Add a new compatible provider and test - litellm.openai_compatible_providers.append("new_provider") - assert InitalizeOpenAISDKClient._should_create_openai_sdk_client_for_model( - "custom-model", "new_provider" - ), "Should return True for newly added OpenAI compatible provider" - - # Clean up - litellm.openai_compatible_providers.remove("new_provider") - - -def test_get_client_initialization_params_openai(): - """Test basic OpenAI configuration with direct parameter passing.""" - model = {} - model_name = "gpt-3.5-turbo" - custom_llm_provider = None - litellm_params = {"api_key": "sk-openai-key", "timeout": 30, "max_retries": 3} - default_api_key = None - default_api_base = None - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - - assert isinstance(result, OpenAISDKClientInitializationParams) - assert result.api_key == "sk-openai-key" - assert result.timeout == 30 - assert result.max_retries == 3 - assert result.model_name == "gpt-3.5-turbo" - - -def test_get_client_initialization_params_azure(): - """Test Azure OpenAI configuration with specific Azure parameters.""" - model = {} - model_name = "azure/gpt-4" - custom_llm_provider = "azure" - litellm_params = { - "api_key": "azure-key", - "api_base": "https://example.azure.openai.com", - "api_version": "2023-05-15", - } - default_api_key = None - default_api_base = None - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - - assert result.api_key == "azure-key" - assert result.api_base == "https://example.azure.openai.com" - assert result.api_version == "2023-05-15" - assert result.custom_llm_provider == "azure" - - -def test_get_client_initialization_params_environment_variable_parsing(): - """Test parsing of environment variables for configuration.""" - os.environ["UNIQUE_OPENAI_API_KEY"] = "env-openai-key" - os.environ["UNIQUE_TIMEOUT"] = "45" - - model = {} - model_name = "gpt-4" - custom_llm_provider = None - litellm_params = { - "api_key": "os.environ/UNIQUE_OPENAI_API_KEY", - "timeout": "os.environ/UNIQUE_TIMEOUT", - "organization": "os.environ/UNIQUE_ORG_ID", - } - default_api_key = None - default_api_base = None - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - - assert result.api_key == "env-openai-key" - assert result.timeout == 45.0 - assert result.organization is None # Since ORG_ID is not set in the environment - - -def test_get_client_initialization_params_azure_ai_studio_mistral(): - """ - Test configuration for Azure AI Studio Mistral model. - - - /v1/ is added to the api_base if it is not present - - custom_llm_provider is set to openai (Azure AI Studio Mistral models need to use OpenAI route) - """ - - model = {} - model_name = "azure/mistral-large-latest" - custom_llm_provider = "azure" - litellm_params = { - "api_key": "azure-key", - "api_base": "https://example.azure.openai.com", - } - default_api_key = None - default_api_base = None - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model, - model_name, - custom_llm_provider, - litellm_params, - default_api_key, - default_api_base, - ) - - assert result.custom_llm_provider == "openai" - assert result.model_name == "mistral-large-latest" - assert result.api_base == "https://example.azure.openai.com/v1/" - - -def test_get_client_initialization_params_default_values(): - """ - Test use of default values when specific parameters are not provided. - - This is used typically for OpenAI compatible providers - example Together AI - - """ - model = {} - model_name = "together/meta-llama-3.1-8b-instruct" - custom_llm_provider = None - litellm_params = {} - default_api_key = "together-api-key" - default_api_base = "https://together.xyz/api.openai.com" - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - - assert result.api_key == "together-api-key" - assert result.api_base == "https://together.xyz/api.openai.com" - assert result.timeout == litellm.request_timeout - assert result.max_retries == 0 - - -def test_get_client_initialization_params_all_env_vars(): - # Set up environment variables - os.environ["TEST_API_KEY"] = "test-api-key" - os.environ["TEST_API_BASE"] = "https://test.openai.com" - os.environ["TEST_API_VERSION"] = "2023-05-15" - os.environ["TEST_TIMEOUT"] = "30" - os.environ["TEST_STREAM_TIMEOUT"] = "60" - os.environ["TEST_MAX_RETRIES"] = "3" - os.environ["TEST_ORGANIZATION"] = "test-org" - - model = {} - model_name = "gpt-4" - custom_llm_provider = None - litellm_params = { - "api_key": "os.environ/TEST_API_KEY", - "api_base": "os.environ/TEST_API_BASE", - "api_version": "os.environ/TEST_API_VERSION", - "timeout": "os.environ/TEST_TIMEOUT", - "stream_timeout": "os.environ/TEST_STREAM_TIMEOUT", - "max_retries": "os.environ/TEST_MAX_RETRIES", - "organization": "os.environ/TEST_ORGANIZATION", - } - default_api_key = None - default_api_base = None - - result = InitalizeOpenAISDKClient._get_client_initialization_params( - model=model, - model_name=model_name, - custom_llm_provider=custom_llm_provider, - litellm_params=litellm_params, - default_api_key=default_api_key, - default_api_base=default_api_base, - ) - - assert isinstance(result, OpenAISDKClientInitializationParams) - assert result.api_key == "test-api-key" - assert result.api_base == "https://test.openai.com" - assert result.api_version == "2023-05-15" - assert result.timeout == 30.0 - assert result.stream_timeout == 60.0 - assert result.max_retries == 3 - assert result.organization == "test-org" - assert result.model_name == "gpt-4" - assert result.custom_llm_provider is None - - # Clean up environment variables - for key in [ - "TEST_API_KEY", - "TEST_API_BASE", - "TEST_API_VERSION", - "TEST_TIMEOUT", - "TEST_STREAM_TIMEOUT", - "TEST_MAX_RETRIES", - "TEST_ORGANIZATION", - ]: - os.environ.pop(key) From 182adec7d020fc01ea7ee504e7eee9b50b6f1a7d Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 23:27:19 +0530 Subject: [PATCH 22/62] def test_text_completion_with_echo(stream): (#6401) test --- litellm/types/utils.py | 4 +- .../test_text_completion_unit_tests.py | 64 +++++++++++++++++++ tests/local_testing/test_text_completion.py | 21 ++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 tests/llm_translation/test_text_completion_unit_tests.py diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 28a37e88d1..341c9fc8b8 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -970,9 +970,9 @@ class EmbeddingResponse(OpenAIObject): class Logprobs(OpenAIObject): text_offset: List[int] - token_logprobs: List[float] + token_logprobs: List[Union[float, None]] tokens: List[str] - top_logprobs: List[Dict[str, float]] + top_logprobs: List[Union[Dict[str, float], None]] class TextChoices(OpenAIObject): diff --git a/tests/llm_translation/test_text_completion_unit_tests.py b/tests/llm_translation/test_text_completion_unit_tests.py new file mode 100644 index 0000000000..2012ae11b2 --- /dev/null +++ b/tests/llm_translation/test_text_completion_unit_tests.py @@ -0,0 +1,64 @@ +import json +import os +import sys +from datetime import datetime +from unittest.mock import AsyncMock + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system path + +from litellm.types.utils import TextCompletionResponse + + +def test_convert_dict_to_text_completion_response(): + input_dict = { + "id": "cmpl-ALVLPJgRkqpTomotoOMi3j0cAaL4L", + "choices": [ + { + "finish_reason": "length", + "index": 0, + "logprobs": { + "text_offset": [0, 5], + "token_logprobs": [None, -12.203847], + "tokens": ["hello", " crisp"], + "top_logprobs": [None, {",": -2.1568563}], + }, + "text": "hello crisp", + } + ], + "created": 1729688739, + "model": "davinci-002", + "object": "text_completion", + "system_fingerprint": None, + "usage": { + "completion_tokens": 1, + "prompt_tokens": 1, + "total_tokens": 2, + "completion_tokens_details": None, + "prompt_tokens_details": None, + }, + } + + response = TextCompletionResponse(**input_dict) + + assert response.id == "cmpl-ALVLPJgRkqpTomotoOMi3j0cAaL4L" + assert len(response.choices) == 1 + assert response.choices[0].finish_reason == "length" + assert response.choices[0].index == 0 + assert response.choices[0].text == "hello crisp" + assert response.created == 1729688739 + assert response.model == "davinci-002" + assert response.object == "text_completion" + assert response.system_fingerprint is None + assert response.usage.completion_tokens == 1 + assert response.usage.prompt_tokens == 1 + assert response.usage.total_tokens == 2 + assert response.usage.completion_tokens_details is None + assert response.usage.prompt_tokens_details is None + + # Test logprobs + assert response.choices[0].logprobs.text_offset == [0, 5] + assert response.choices[0].logprobs.token_logprobs == [None, -12.203847] + assert response.choices[0].logprobs.tokens == ["hello", " crisp"] + assert response.choices[0].logprobs.top_logprobs == [None, {",": -2.1568563}] diff --git a/tests/local_testing/test_text_completion.py b/tests/local_testing/test_text_completion.py index 76d1dbb19e..6059d60bc2 100644 --- a/tests/local_testing/test_text_completion.py +++ b/tests/local_testing/test_text_completion.py @@ -4259,3 +4259,24 @@ def test_completion_fireworks_ai_multiple_choices(): print(response.choices) assert len(response.choices) == 4 + + +@pytest.mark.parametrize("stream", [True, False]) +def test_text_completion_with_echo(stream): + litellm.set_verbose = True + response = litellm.text_completion( + model="davinci-002", + prompt="hello", + max_tokens=1, # only see the first token + stop="\n", # stop at the first newline + logprobs=1, # return log prob + echo=True, # if True, return the prompt as well + stream=stream, + ) + print(response) + + if stream: + for chunk in response: + print(chunk) + else: + assert isinstance(response, TextCompletionResponse) From bcce21a5b0568ee76be52de3775c8ae5d9ac60a4 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Wed, 23 Oct 2024 23:36:39 +0530 Subject: [PATCH 23/62] fix linting - remove # noqa PLR0915 from fixed function --- litellm/caching/caching.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/caching/caching.py b/litellm/caching/caching.py index 85fbe32345..f845633cb9 100644 --- a/litellm/caching/caching.py +++ b/litellm/caching/caching.py @@ -233,7 +233,7 @@ class Cache: if self.namespace is not None and isinstance(self.cache, RedisCache): self.cache.namespace = self.namespace - def get_cache_key(self, *args, **kwargs) -> str: # noqa: PLR0915 + def get_cache_key(self, *args, **kwargs) -> str: """ Get the cache key for the given arguments. From ca09f4afec4b8a415c398b58dbb88fae8979176b Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 23 Oct 2024 22:19:57 -0700 Subject: [PATCH 24/62] test: cleanup codestral tests - backend api unavailable --- tests/local_testing/test_completion.py | 1 + tests/local_testing/test_text_completion.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/local_testing/test_completion.py b/tests/local_testing/test_completion.py index b908c7b5c8..a4d5e8c0bc 100644 --- a/tests/local_testing/test_completion.py +++ b/tests/local_testing/test_completion.py @@ -1088,6 +1088,7 @@ def test_completion_mistral_api(): pytest.fail(f"Error occurred: {e}") +@pytest.mark.skip(reason="backend api unavailable") @pytest.mark.asyncio async def test_completion_codestral_chat_api(): try: diff --git a/tests/local_testing/test_text_completion.py b/tests/local_testing/test_text_completion.py index 6059d60bc2..c1e4d0e168 100644 --- a/tests/local_testing/test_text_completion.py +++ b/tests/local_testing/test_text_completion.py @@ -4109,9 +4109,7 @@ async def test_async_text_completion_chat_model_stream(): # asyncio.run(test_async_text_completion_chat_model_stream()) -@pytest.mark.parametrize( - "model", ["vertex_ai/codestral@2405", "text-completion-codestral/codestral-2405"] # -) +@pytest.mark.parametrize("model", ["vertex_ai/codestral@2405"]) # @pytest.mark.asyncio async def test_completion_codestral_fim_api(model): try: @@ -4154,7 +4152,7 @@ async def test_completion_codestral_fim_api(model): @pytest.mark.parametrize( "model", - ["vertex_ai/codestral@2405", "text-completion-codestral/codestral-2405"], + ["vertex_ai/codestral@2405"], ) @pytest.mark.asyncio async def test_completion_codestral_fim_api_stream(model): From cdda7c243fd4860668b4d10c3b15f7f3d51c2527 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 24 Oct 2024 16:41:09 +0400 Subject: [PATCH 25/62] (refactor) prometheus async_log_success_event to be under 100 LOC (#6416) * unit testig for prometheus * unit testing for success metrics * use 1 helper for _increment_token_metrics * use helper for _increment_remaining_budget_metrics * use _increment_remaining_budget_metrics * use _increment_top_level_request_and_spend_metrics * use helper for _set_latency_metrics * remove noqa violation * fix test prometheus * test prometheus * unit testing for all prometheus helper functions * fix prom unit tests * fix unit tests prometheus * fix unit test prom --- litellm/integrations/prometheus.py | 253 +++++++++---- tests/local_testing/test_prometheus.py | 8 + .../test_prometheus_unit_tests.py | 344 ++++++++++++++++++ .../test_unit_tests_init_callbacks.py | 12 + 4 files changed, 540 insertions(+), 77 deletions(-) create mode 100644 tests/logging_callback_tests/test_prometheus_unit_tests.py diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index a929847062..8a4f409b6c 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -6,7 +6,7 @@ import subprocess import sys import traceback import uuid -from datetime import datetime, timedelta +from datetime import date, datetime, timedelta from typing import Optional, TypedDict, Union import dotenv @@ -334,13 +334,8 @@ class PrometheusLogger(CustomLogger): print_verbose(f"Got exception on init prometheus client {str(e)}") raise e - async def async_log_success_event( # noqa: PLR0915 - self, kwargs, response_obj, start_time, end_time - ): + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): # Define prometheus client - from litellm.proxy.common_utils.callback_utils import ( - get_model_group_from_litellm_kwargs, - ) from litellm.types.utils import StandardLoggingPayload verbose_logger.debug( @@ -358,7 +353,6 @@ class PrometheusLogger(CustomLogger): _metadata = litellm_params.get("metadata", {}) proxy_server_request = litellm_params.get("proxy_server_request") or {} end_user_id = proxy_server_request.get("body", {}).get("user", None) - model_parameters: dict = standard_logging_payload["model_parameters"] user_id = standard_logging_payload["metadata"]["user_api_key_user_id"] user_api_key = standard_logging_payload["metadata"]["user_api_key_hash"] user_api_key_alias = standard_logging_payload["metadata"]["user_api_key_alias"] @@ -369,25 +363,6 @@ class PrometheusLogger(CustomLogger): output_tokens = standard_logging_payload["completion_tokens"] tokens_used = standard_logging_payload["total_tokens"] response_cost = standard_logging_payload["response_cost"] - _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 - ) - - _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 - ) print_verbose( f"inside track_prometheus_metrics, model {model}, response_cost {response_cost}, tokens_used {tokens_used}, end_user_id {end_user_id}, user_api_key {user_api_key}" @@ -402,24 +377,76 @@ class PrometheusLogger(CustomLogger): user_api_key = hash_token(user_api_key) - self.litellm_requests_metric.labels( - end_user_id, - user_api_key, - user_api_key_alias, - model, - user_api_team, - user_api_team_alias, - user_id, - ).inc() - self.litellm_spend_metric.labels( - end_user_id, - user_api_key, - user_api_key_alias, - model, - user_api_team, - user_api_team_alias, - user_id, - ).inc(response_cost) + # increment total LLM requests and spend metric + self._increment_top_level_request_and_spend_metrics( + end_user_id=end_user_id, + user_api_key=user_api_key, + user_api_key_alias=user_api_key_alias, + model=model, + user_api_team=user_api_team, + user_api_team_alias=user_api_team_alias, + user_id=user_id, + response_cost=response_cost, + ) + + # input, output, total token metrics + self._increment_token_metrics( + standard_logging_payload=standard_logging_payload, + end_user_id=end_user_id, + user_api_key=user_api_key, + user_api_key_alias=user_api_key_alias, + model=model, + user_api_team=user_api_team, + user_api_team_alias=user_api_team_alias, + user_id=user_id, + ) + + # remaining budget metrics + self._increment_remaining_budget_metrics( + user_api_team=user_api_team, + user_api_team_alias=user_api_team_alias, + user_api_key=user_api_key, + user_api_key_alias=user_api_key_alias, + litellm_params=litellm_params, + ) + + # set proxy virtual key rpm/tpm metrics + self._set_virtual_key_rate_limit_metrics( + user_api_key=user_api_key, + user_api_key_alias=user_api_key_alias, + kwargs=kwargs, + metadata=_metadata, + ) + + # set latency metrics + self._set_latency_metrics( + kwargs=kwargs, + model=model, + user_api_key=user_api_key, + user_api_key_alias=user_api_key_alias, + user_api_team=user_api_team, + user_api_team_alias=user_api_team_alias, + standard_logging_payload=standard_logging_payload, + ) + + # set x-ratelimit headers + self.set_llm_deployment_success_metrics( + kwargs, start_time, end_time, output_tokens + ) + pass + + def _increment_token_metrics( + self, + standard_logging_payload: StandardLoggingPayload, + end_user_id: Optional[str], + user_api_key: Optional[str], + user_api_key_alias: Optional[str], + model: Optional[str], + user_api_team: Optional[str], + user_api_team_alias: Optional[str], + user_id: Optional[str], + ): + # token metrics self.litellm_tokens_metric.labels( end_user_id, user_api_key, @@ -450,6 +477,34 @@ class PrometheusLogger(CustomLogger): user_id, ).inc(standard_logging_payload["completion_tokens"]) + def _increment_remaining_budget_metrics( + self, + user_api_team: Optional[str], + user_api_team_alias: Optional[str], + user_api_key: Optional[str], + user_api_key_alias: Optional[str], + litellm_params: dict, + ): + _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 = self._safe_get_remaining_budget( + 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 = self._safe_get_remaining_budget( + max_budget=_api_key_max_budget, spend=_api_key_spend + ) + # Remaining Budget Metrics self.litellm_remaining_team_budget_metric.labels( user_api_team, user_api_team_alias ).set(_remaining_team_budget) @@ -458,6 +513,47 @@ class PrometheusLogger(CustomLogger): user_api_key, user_api_key_alias ).set(_remaining_api_key_budget) + def _increment_top_level_request_and_spend_metrics( + self, + end_user_id: Optional[str], + user_api_key: Optional[str], + user_api_key_alias: Optional[str], + model: Optional[str], + user_api_team: Optional[str], + user_api_team_alias: Optional[str], + user_id: Optional[str], + response_cost: float, + ): + self.litellm_requests_metric.labels( + end_user_id, + user_api_key, + user_api_key_alias, + model, + user_api_team, + user_api_team_alias, + user_id, + ).inc() + self.litellm_spend_metric.labels( + end_user_id, + user_api_key, + user_api_key_alias, + model, + user_api_team, + user_api_team_alias, + user_id, + ).inc(response_cost) + + def _set_virtual_key_rate_limit_metrics( + self, + user_api_key: Optional[str], + user_api_key_alias: Optional[str], + kwargs: dict, + metadata: dict, + ): + from litellm.proxy.common_utils.callback_utils import ( + get_model_group_from_litellm_kwargs, + ) + # Set remaining rpm/tpm for API Key + model # see parallel_request_limiter.py - variables are set there model_group = get_model_group_from_litellm_kwargs(kwargs) @@ -466,10 +562,8 @@ class PrometheusLogger(CustomLogger): ) remaining_tokens_variable_name = f"litellm-key-remaining-tokens-{model_group}" - remaining_requests = _metadata.get( - remaining_requests_variable_name, sys.maxsize - ) - remaining_tokens = _metadata.get(remaining_tokens_variable_name, sys.maxsize) + remaining_requests = metadata.get(remaining_requests_variable_name, sys.maxsize) + remaining_tokens = metadata.get(remaining_tokens_variable_name, sys.maxsize) self.litellm_remaining_api_key_requests_for_model.labels( user_api_key, user_api_key_alias, model_group @@ -479,9 +573,20 @@ class PrometheusLogger(CustomLogger): user_api_key, user_api_key_alias, model_group ).set(remaining_tokens) + def _set_latency_metrics( + self, + kwargs: dict, + model: Optional[str], + user_api_key: Optional[str], + user_api_key_alias: Optional[str], + user_api_team: Optional[str], + user_api_team_alias: Optional[str], + standard_logging_payload: StandardLoggingPayload, + ): # latency metrics - total_time: timedelta = kwargs.get("end_time") - kwargs.get("start_time") - total_time_seconds = total_time.total_seconds() + model_parameters: dict = standard_logging_payload["model_parameters"] + end_time: datetime = kwargs.get("end_time") or datetime.now() + start_time: Optional[datetime] = kwargs.get("start_time") api_call_start_time = kwargs.get("api_call_start_time", None) completion_start_time = kwargs.get("completion_start_time", None) @@ -509,9 +614,7 @@ class PrometheusLogger(CustomLogger): if api_call_start_time is not None and isinstance( api_call_start_time, datetime ): - api_call_total_time: timedelta = ( - kwargs.get("end_time") - api_call_start_time - ) + api_call_total_time: timedelta = end_time - api_call_start_time api_call_total_time_seconds = api_call_total_time.total_seconds() self.litellm_llm_api_latency_metric.labels( model, @@ -521,20 +624,17 @@ class PrometheusLogger(CustomLogger): user_api_team_alias, ).observe(api_call_total_time_seconds) - # log metrics - self.litellm_request_total_latency_metric.labels( - model, - user_api_key, - user_api_key_alias, - user_api_team, - user_api_team_alias, - ).observe(total_time_seconds) - - # set x-ratelimit headers - self.set_llm_deployment_success_metrics( - kwargs, start_time, end_time, output_tokens - ) - pass + # total request latency + if start_time is not None and isinstance(start_time, datetime): + total_time: timedelta = end_time - start_time + total_time_seconds = total_time.total_seconds() + self.litellm_request_total_latency_metric.labels( + model, + user_api_key, + user_api_key_alias, + user_api_team, + user_api_team_alias, + ).observe(total_time_seconds) async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): from litellm.types.utils import StandardLoggingPayload @@ -1007,14 +1107,13 @@ class PrometheusLogger(CustomLogger): litellm_model_name, model_id, api_base, api_provider, exception_status ).inc() + def _safe_get_remaining_budget( + self, max_budget: Optional[float], spend: Optional[float] + ) -> float: + if max_budget is None: + return float("inf") -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 - if spend is None: - return max_budget - - return max_budget - spend + return max_budget - spend diff --git a/tests/local_testing/test_prometheus.py b/tests/local_testing/test_prometheus.py index 2f0e4a19e0..164d94553a 100644 --- a/tests/local_testing/test_prometheus.py +++ b/tests/local_testing/test_prometheus.py @@ -16,6 +16,14 @@ from litellm import completion from litellm._logging import verbose_logger from litellm.integrations.prometheus import PrometheusLogger from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler +from litellm.types.utils import ( + StandardLoggingPayload, + StandardLoggingMetadata, + StandardLoggingHiddenParams, + StandardLoggingModelInformation, +) +from unittest.mock import MagicMock, patch +from datetime import datetime, timedelta verbose_logger.setLevel(logging.DEBUG) diff --git a/tests/logging_callback_tests/test_prometheus_unit_tests.py b/tests/logging_callback_tests/test_prometheus_unit_tests.py new file mode 100644 index 0000000000..0355692737 --- /dev/null +++ b/tests/logging_callback_tests/test_prometheus_unit_tests.py @@ -0,0 +1,344 @@ +import io +import os +import sys + +sys.path.insert(0, os.path.abspath("../..")) + +import asyncio +import logging +import uuid + +import pytest +from prometheus_client import REGISTRY, CollectorRegistry + +import litellm +from litellm import completion +from litellm._logging import verbose_logger +from litellm.integrations.prometheus import PrometheusLogger +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler +from litellm.types.utils import ( + StandardLoggingPayload, + StandardLoggingMetadata, + StandardLoggingHiddenParams, + StandardLoggingModelInformation, +) +import pytest +from unittest.mock import MagicMock, patch +from datetime import datetime, timedelta +from litellm.integrations.prometheus import PrometheusLogger + +verbose_logger.setLevel(logging.DEBUG) + +litellm.set_verbose = True +import time + + +@pytest.fixture +def prometheus_logger(): + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + REGISTRY.unregister(collector) + return PrometheusLogger() + + +def create_standard_logging_payload() -> StandardLoggingPayload: + return StandardLoggingPayload( + id="test_id", + call_type="completion", + response_cost=0.1, + response_cost_failure_debug_info=None, + status="success", + total_tokens=30, + prompt_tokens=20, + completion_tokens=10, + startTime=1234567890.0, + endTime=1234567891.0, + completionStartTime=1234567890.5, + model_map_information=StandardLoggingModelInformation( + model_map_key="gpt-3.5-turbo", model_map_value=None + ), + model="gpt-3.5-turbo", + model_id="model-123", + model_group="openai-gpt", + api_base="https://api.openai.com", + metadata=StandardLoggingMetadata( + user_api_key_hash="test_hash", + user_api_key_alias="test_alias", + user_api_key_team_id="test_team", + user_api_key_user_id="test_user", + user_api_key_team_alias="test_team_alias", + spend_logs_metadata=None, + requester_ip_address="127.0.0.1", + requester_metadata=None, + ), + cache_hit=False, + cache_key=None, + saved_cache_cost=0.0, + request_tags=[], + end_user=None, + requester_ip_address="127.0.0.1", + messages=[{"role": "user", "content": "Hello, world!"}], + response={"choices": [{"message": {"content": "Hi there!"}}]}, + error_str=None, + model_parameters={"stream": True}, + hidden_params=StandardLoggingHiddenParams( + model_id="model-123", + cache_key=None, + api_base="https://api.openai.com", + response_cost="0.1", + additional_headers=None, + ), + ) + + +def test_safe_get_remaining_budget(prometheus_logger): + assert prometheus_logger._safe_get_remaining_budget(100, 30) == 70 + assert prometheus_logger._safe_get_remaining_budget(100, None) == 100 + assert prometheus_logger._safe_get_remaining_budget(None, 30) == float("inf") + assert prometheus_logger._safe_get_remaining_budget(None, None) == float("inf") + + +@pytest.mark.asyncio +async def test_async_log_success_event(prometheus_logger): + standard_logging_object = create_standard_logging_payload() + kwargs = { + "model": "gpt-3.5-turbo", + "litellm_params": { + "metadata": { + "user_api_key": "test_key", + "user_api_key_user_id": "test_user", + "user_api_key_team_id": "test_team", + } + }, + "start_time": datetime.now(), + "completion_start_time": datetime.now(), + "api_call_start_time": datetime.now(), + "end_time": datetime.now() + timedelta(seconds=1), + "standard_logging_object": standard_logging_object, + } + response_obj = MagicMock() + + # Mock the prometheus client methods + + # High Level Metrics - request/spend + prometheus_logger.litellm_requests_metric = MagicMock() + prometheus_logger.litellm_spend_metric = MagicMock() + + # Token Metrics + prometheus_logger.litellm_tokens_metric = MagicMock() + prometheus_logger.litellm_input_tokens_metric = MagicMock() + prometheus_logger.litellm_output_tokens_metric = MagicMock() + + # Remaining Budget Metrics + prometheus_logger.litellm_remaining_team_budget_metric = MagicMock() + prometheus_logger.litellm_remaining_api_key_budget_metric = MagicMock() + + # Virtual Key Rate limit Metrics + prometheus_logger.litellm_remaining_api_key_requests_for_model = MagicMock() + prometheus_logger.litellm_remaining_api_key_tokens_for_model = MagicMock() + + # Latency Metrics + prometheus_logger.litellm_llm_api_time_to_first_token_metric = MagicMock() + prometheus_logger.litellm_llm_api_latency_metric = MagicMock() + prometheus_logger.litellm_request_total_latency_metric = MagicMock() + + await prometheus_logger.async_log_success_event( + kwargs, response_obj, kwargs["start_time"], kwargs["end_time"] + ) + + # Assert that the metrics were incremented + prometheus_logger.litellm_requests_metric.labels.assert_called() + prometheus_logger.litellm_spend_metric.labels.assert_called() + + # Token Metrics + prometheus_logger.litellm_tokens_metric.labels.assert_called() + prometheus_logger.litellm_input_tokens_metric.labels.assert_called() + prometheus_logger.litellm_output_tokens_metric.labels.assert_called() + + # Remaining Budget Metrics + prometheus_logger.litellm_remaining_team_budget_metric.labels.assert_called() + prometheus_logger.litellm_remaining_api_key_budget_metric.labels.assert_called() + + # Virtual Key Rate limit Metrics + prometheus_logger.litellm_remaining_api_key_requests_for_model.labels.assert_called() + prometheus_logger.litellm_remaining_api_key_tokens_for_model.labels.assert_called() + + # Latency Metrics + prometheus_logger.litellm_llm_api_time_to_first_token_metric.labels.assert_called() + prometheus_logger.litellm_llm_api_latency_metric.labels.assert_called() + prometheus_logger.litellm_request_total_latency_metric.labels.assert_called() + + +def test_increment_token_metrics(prometheus_logger): + """ + Test the increment_token_metrics method + + input, output, and total tokens metrics are incremented by the values in the standard logging payload + """ + prometheus_logger.litellm_tokens_metric = MagicMock() + prometheus_logger.litellm_input_tokens_metric = MagicMock() + prometheus_logger.litellm_output_tokens_metric = MagicMock() + + standard_logging_payload = create_standard_logging_payload() + standard_logging_payload["total_tokens"] = 100 + standard_logging_payload["prompt_tokens"] = 50 + standard_logging_payload["completion_tokens"] = 50 + + prometheus_logger._increment_token_metrics( + standard_logging_payload, + end_user_id="user1", + user_api_key="key1", + user_api_key_alias="alias1", + model="gpt-3.5-turbo", + user_api_team="team1", + user_api_team_alias="team_alias1", + user_id="user1", + ) + + prometheus_logger.litellm_tokens_metric.labels.assert_called_once_with( + "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" + ) + prometheus_logger.litellm_tokens_metric.labels().inc.assert_called_once_with(100) + + prometheus_logger.litellm_input_tokens_metric.labels.assert_called_once_with( + "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" + ) + prometheus_logger.litellm_input_tokens_metric.labels().inc.assert_called_once_with( + 50 + ) + + prometheus_logger.litellm_output_tokens_metric.labels.assert_called_once_with( + "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" + ) + prometheus_logger.litellm_output_tokens_metric.labels().inc.assert_called_once_with( + 50 + ) + + +def test_increment_remaining_budget_metrics(prometheus_logger): + """ + Test the increment_remaining_budget_metrics method + + team and api key budget metrics are set to the difference between max budget and spend + """ + prometheus_logger.litellm_remaining_team_budget_metric = MagicMock() + prometheus_logger.litellm_remaining_api_key_budget_metric = MagicMock() + + litellm_params = { + "metadata": { + "user_api_key_team_spend": 50, + "user_api_key_team_max_budget": 100, + "user_api_key_spend": 25, + "user_api_key_max_budget": 75, + } + } + + prometheus_logger._increment_remaining_budget_metrics( + user_api_team="team1", + user_api_team_alias="team_alias1", + user_api_key="key1", + user_api_key_alias="alias1", + litellm_params=litellm_params, + ) + + prometheus_logger.litellm_remaining_team_budget_metric.labels.assert_called_once_with( + "team1", "team_alias1" + ) + prometheus_logger.litellm_remaining_team_budget_metric.labels().set.assert_called_once_with( + 50 + ) + + prometheus_logger.litellm_remaining_api_key_budget_metric.labels.assert_called_once_with( + "key1", "alias1" + ) + prometheus_logger.litellm_remaining_api_key_budget_metric.labels().set.assert_called_once_with( + 50 + ) + + +def test_set_latency_metrics(prometheus_logger): + """ + Test the set_latency_metrics method + + time to first token, llm api latency, and request total latency metrics are set to the values in the standard logging payload + """ + standard_logging_payload = create_standard_logging_payload() + standard_logging_payload["model_parameters"] = {"stream": True} + prometheus_logger.litellm_llm_api_time_to_first_token_metric = MagicMock() + prometheus_logger.litellm_llm_api_latency_metric = MagicMock() + prometheus_logger.litellm_request_total_latency_metric = MagicMock() + + now = datetime.now() + kwargs = { + "end_time": now, # when the request ends + "start_time": now - timedelta(seconds=2), # when the request starts + "api_call_start_time": now - timedelta(seconds=1.5), # when the api call starts + "completion_start_time": now + - timedelta(seconds=1), # when the completion starts + } + + prometheus_logger._set_latency_metrics( + kwargs=kwargs, + model="gpt-3.5-turbo", + user_api_key="key1", + user_api_key_alias="alias1", + user_api_team="team1", + user_api_team_alias="team_alias1", + standard_logging_payload=standard_logging_payload, + ) + + # completion_start_time - api_call_start_time + prometheus_logger.litellm_llm_api_time_to_first_token_metric.labels.assert_called_once_with( + "gpt-3.5-turbo", "key1", "alias1", "team1", "team_alias1" + ) + prometheus_logger.litellm_llm_api_time_to_first_token_metric.labels().observe.assert_called_once_with( + 0.5 + ) + + # end_time - api_call_start_time + prometheus_logger.litellm_llm_api_latency_metric.labels.assert_called_once_with( + "gpt-3.5-turbo", "key1", "alias1", "team1", "team_alias1" + ) + prometheus_logger.litellm_llm_api_latency_metric.labels().observe.assert_called_once_with( + 1.5 + ) + + # total latency for the request + prometheus_logger.litellm_request_total_latency_metric.labels.assert_called_once_with( + "gpt-3.5-turbo", "key1", "alias1", "team1", "team_alias1" + ) + prometheus_logger.litellm_request_total_latency_metric.labels().observe.assert_called_once_with( + 2.0 + ) + + +def test_increment_top_level_request_and_spend_metrics(prometheus_logger): + """ + Test the increment_top_level_request_and_spend_metrics method + + - litellm_requests_metric is incremented by 1 + - litellm_spend_metric is incremented by the response cost in the standard logging payload + """ + prometheus_logger.litellm_requests_metric = MagicMock() + prometheus_logger.litellm_spend_metric = MagicMock() + + prometheus_logger._increment_top_level_request_and_spend_metrics( + end_user_id="user1", + user_api_key="key1", + user_api_key_alias="alias1", + model="gpt-3.5-turbo", + user_api_team="team1", + user_api_team_alias="team_alias1", + user_id="user1", + response_cost=0.1, + ) + + prometheus_logger.litellm_requests_metric.labels.assert_called_once_with( + "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" + ) + prometheus_logger.litellm_requests_metric.labels().inc.assert_called_once() + + prometheus_logger.litellm_spend_metric.labels.assert_called_once_with( + "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" + ) + prometheus_logger.litellm_spend_metric.labels().inc.assert_called_once_with(0.1) diff --git a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py index e0a7e85f51..5b14717dc1 100644 --- a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py +++ b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py @@ -17,6 +17,7 @@ import litellm import asyncio import logging from litellm._logging import verbose_logger +from prometheus_client import REGISTRY, CollectorRegistry from litellm.integrations.lago import LagoLogger from litellm.integrations.openmeter import OpenMeterLogger @@ -33,6 +34,12 @@ from litellm.integrations.argilla import ArgillaLogger from litellm.proxy.hooks.dynamic_rate_limiter import _PROXY_DynamicRateLimitHandler from unittest.mock import patch +# clear prometheus collectors / registry +collectors = list(REGISTRY._collector_to_names.keys()) +for collector in collectors: + REGISTRY.unregister(collector) +###################################### + callback_class_str_to_classType = { "lago": LagoLogger, "openmeter": OpenMeterLogger, @@ -111,6 +118,11 @@ async def use_callback_in_llm_call( elif callback == "openmeter": # it's currently handled in jank way, TODO: fix openmete and then actually run it's test return + elif callback == "prometheus": + # pytest teardown - clear existing prometheus collectors + collectors = list(REGISTRY._collector_to_names.keys()) + for collector in collectors: + REGISTRY.unregister(collector) # Mock the httpx call for Argilla dataset retrieval if callback == "argilla": From 17e81d861cf184e8be49042a7dfda1a2301b296b Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 24 Oct 2024 19:26:46 +0400 Subject: [PATCH 26/62] (refactor) router - use static methods for client init utils (#6420) * use InitalizeOpenAISDKClient * use InitalizeOpenAISDKClient static method * fix # noqa: PLR0915 --- litellm/router.py | 23 +- .../client_initalization_utils.py | 851 +++++++++--------- 2 files changed, 448 insertions(+), 426 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 142a781bbe..0cad565b00 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -63,10 +63,7 @@ from litellm.router_utils.batch_utils import ( _get_router_metadata_variable_name, replace_model_in_jsonl, ) -from litellm.router_utils.client_initalization_utils import ( - set_client, - should_initialize_sync_client, -) +from litellm.router_utils.client_initalization_utils import InitalizeOpenAISDKClient from litellm.router_utils.cooldown_cache import CooldownCache from litellm.router_utils.cooldown_callbacks import router_cooldown_event_callback from litellm.router_utils.cooldown_handlers import ( @@ -3951,7 +3948,7 @@ class Router: raise Exception(f"Unsupported provider - {custom_llm_provider}") # init OpenAI, Azure clients - set_client( + InitalizeOpenAISDKClient.set_client( litellm_router_instance=self, model=deployment.to_json(exclude_none=True) ) @@ -4661,7 +4658,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4671,7 +4670,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key, local_only=True) return client else: @@ -4682,7 +4683,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key) return client else: @@ -4692,7 +4695,9 @@ class Router: """ Re-initialize the client """ - set_client(litellm_router_instance=self, model=deployment) + InitalizeOpenAISDKClient.set_client( + litellm_router_instance=self, model=deployment + ) client = self.cache.get_cache(key=cache_key) return client diff --git a/litellm/router_utils/client_initalization_utils.py b/litellm/router_utils/client_initalization_utils.py index 6c845296a8..db8f20ee6b 100644 --- a/litellm/router_utils/client_initalization_utils.py +++ b/litellm/router_utils/client_initalization_utils.py @@ -23,236 +23,227 @@ else: LitellmRouter = Any -def should_initialize_sync_client( - litellm_router_instance: LitellmRouter, -) -> bool: - """ - Returns if Sync OpenAI, Azure Clients should be initialized. +class InitalizeOpenAISDKClient: + @staticmethod + def should_initialize_sync_client( + litellm_router_instance: LitellmRouter, + ) -> bool: + """ + Returns if Sync OpenAI, Azure Clients should be initialized. - Do not init sync clients when router.router_general_settings.async_only_mode is True + Do not init sync clients when router.router_general_settings.async_only_mode is True - """ - if litellm_router_instance is None: - return False - - if litellm_router_instance.router_general_settings is not None: - if ( - hasattr(litellm_router_instance, "router_general_settings") - and hasattr( - litellm_router_instance.router_general_settings, "async_only_mode" - ) - and litellm_router_instance.router_general_settings.async_only_mode is True - ): + """ + if litellm_router_instance is None: return False - return True + if litellm_router_instance.router_general_settings is not None: + if ( + hasattr(litellm_router_instance, "router_general_settings") + and hasattr( + litellm_router_instance.router_general_settings, "async_only_mode" + ) + and litellm_router_instance.router_general_settings.async_only_mode + is True + ): + return False + return True -def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PLR0915 - """ - - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 - - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 - """ - client_ttl = litellm_router_instance.client_ttl - litellm_params = model.get("litellm_params", {}) - model_name = litellm_params.get("model") - model_id = model["model_info"]["id"] - # ### IF RPM SET - initialize a semaphore ### - rpm = litellm_params.get("rpm", None) - tpm = litellm_params.get("tpm", None) - max_parallel_requests = litellm_params.get("max_parallel_requests", None) - calculated_max_parallel_requests = calculate_max_parallel_requests( - rpm=rpm, - max_parallel_requests=max_parallel_requests, - tpm=tpm, - default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, - ) - if calculated_max_parallel_requests: - semaphore = asyncio.Semaphore(calculated_max_parallel_requests) - cache_key = f"{model_id}_max_parallel_requests_client" - litellm_router_instance.cache.set_cache( - key=cache_key, - value=semaphore, - local_only=True, - ) - - #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## - custom_llm_provider = litellm_params.get("custom_llm_provider") - custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" - default_api_base = None - default_api_key = None - if custom_llm_provider in litellm.openai_compatible_providers: - _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( - model=model_name - ) - default_api_base = api_base - default_api_key = api_key - - if ( - model_name in litellm.open_ai_chat_completion_models - or custom_llm_provider in litellm.openai_compatible_providers - or custom_llm_provider == "azure" - or custom_llm_provider == "azure_text" - or custom_llm_provider == "custom_openai" - or custom_llm_provider == "openai" - or custom_llm_provider == "text-completion-openai" - or "ft:gpt-3.5-turbo" in model_name - or model_name in litellm.open_ai_embedding_models + @staticmethod + def set_client( # noqa: PLR0915 + litellm_router_instance: LitellmRouter, model: dict ): - is_azure_ai_studio_model: bool = False - if custom_llm_provider == "azure": - if litellm.utils._is_non_openai_azure_model(model_name): - is_azure_ai_studio_model = True - custom_llm_provider = "openai" - # remove azure prefx from model_name - model_name = model_name.replace("azure/", "") - # glorified / complicated reading of configs - # user can pass vars directly or they can pas os.environ/AZURE_API_KEY, in which case we will read the env - # we do this here because we init clients for Azure, OpenAI and we need to set the right key - api_key = litellm_params.get("api_key") or default_api_key - if api_key and isinstance(api_key, str) and api_key.startswith("os.environ/"): - api_key_env_name = api_key.replace("os.environ/", "") - api_key = get_secret_str(api_key_env_name) - litellm_params["api_key"] = api_key - - api_base = litellm_params.get("api_base") - base_url: Optional[str] = litellm_params.get("base_url") - api_base = ( - api_base or base_url or default_api_base - ) # allow users to pass in `api_base` or `base_url` for azure - if api_base and api_base.startswith("os.environ/"): - api_base_env_name = api_base.replace("os.environ/", "") - api_base = get_secret_str(api_base_env_name) - litellm_params["api_base"] = api_base - - ## AZURE AI STUDIO MISTRAL CHECK ## """ - Make sure api base ends in /v1/ - - if not, add it - https://github.com/BerriAI/litellm/issues/2279 + - Initializes Azure/OpenAI clients. Stores them in cache, b/c of this - https://github.com/BerriAI/litellm/issues/1278 + - Initializes Semaphore for client w/ rpm. Stores them in cache. b/c of this - https://github.com/BerriAI/litellm/issues/2994 """ - if ( - is_azure_ai_studio_model is True - and api_base is not None - and isinstance(api_base, str) - and not api_base.endswith("/v1/") - ): - # check if it ends with a trailing slash - if api_base.endswith("/"): - api_base += "v1/" - elif api_base.endswith("/v1"): - api_base += "/" - else: - api_base += "/v1/" - - api_version = litellm_params.get("api_version") - if api_version and api_version.startswith("os.environ/"): - api_version_env_name = api_version.replace("os.environ/", "") - api_version = get_secret_str(api_version_env_name) - litellm_params["api_version"] = api_version - - timeout: Optional[float] = ( - litellm_params.pop("timeout", None) or litellm.request_timeout + client_ttl = litellm_router_instance.client_ttl + litellm_params = model.get("litellm_params", {}) + model_name = litellm_params.get("model") + model_id = model["model_info"]["id"] + # ### IF RPM SET - initialize a semaphore ### + rpm = litellm_params.get("rpm", None) + tpm = litellm_params.get("tpm", None) + max_parallel_requests = litellm_params.get("max_parallel_requests", None) + calculated_max_parallel_requests = calculate_max_parallel_requests( + rpm=rpm, + max_parallel_requests=max_parallel_requests, + tpm=tpm, + default_max_parallel_requests=litellm_router_instance.default_max_parallel_requests, ) - if isinstance(timeout, str) and timeout.startswith("os.environ/"): - timeout_env_name = timeout.replace("os.environ/", "") - timeout = get_secret(timeout_env_name) # type: ignore - litellm_params["timeout"] = timeout - - stream_timeout: Optional[float] = litellm_params.pop( - "stream_timeout", timeout - ) # if no stream_timeout is set, default to timeout - if isinstance(stream_timeout, str) and stream_timeout.startswith("os.environ/"): - stream_timeout_env_name = stream_timeout.replace("os.environ/", "") - stream_timeout = get_secret(stream_timeout_env_name) # type: ignore - litellm_params["stream_timeout"] = stream_timeout - - max_retries: Optional[int] = litellm_params.pop( - "max_retries", 0 - ) # router handles retry logic - if isinstance(max_retries, str) and max_retries.startswith("os.environ/"): - max_retries_env_name = max_retries.replace("os.environ/", "") - max_retries = get_secret(max_retries_env_name) # type: ignore - litellm_params["max_retries"] = max_retries - - organization = litellm_params.get("organization", None) - if isinstance(organization, str) and organization.startswith("os.environ/"): - organization_env_name = organization.replace("os.environ/", "") - organization = get_secret_str(organization_env_name) - litellm_params["organization"] = organization - azure_ad_token_provider: Optional[Callable[[], str]] = None - if litellm_params.get("tenant_id"): - verbose_router_logger.debug("Using Azure AD Token Provider for Azure Auth") - azure_ad_token_provider = get_azure_ad_token_from_entrata_id( - tenant_id=litellm_params.get("tenant_id"), - client_id=litellm_params.get("client_id"), - client_secret=litellm_params.get("client_secret"), + if calculated_max_parallel_requests: + semaphore = asyncio.Semaphore(calculated_max_parallel_requests) + cache_key = f"{model_id}_max_parallel_requests_client" + litellm_router_instance.cache.set_cache( + key=cache_key, + value=semaphore, + local_only=True, ) - if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": - if api_base is None or not isinstance(api_base, str): - filtered_litellm_params = { - k: v for k, v in model["litellm_params"].items() if k != "api_key" - } - _filtered_model = { - "model_name": model["model_name"], - "litellm_params": filtered_litellm_params, - } - raise ValueError( - f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" - ) - azure_ad_token = litellm_params.get("azure_ad_token") - if azure_ad_token is not None: - if azure_ad_token.startswith("oidc/"): - azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) - elif ( - azure_ad_token_provider is None - and litellm.enable_azure_ad_token_refresh is True + #### for OpenAI / Azure we need to initalize the Client for High Traffic ######## + custom_llm_provider = litellm_params.get("custom_llm_provider") + custom_llm_provider = custom_llm_provider or model_name.split("/", 1)[0] or "" + default_api_base = None + default_api_key = None + if custom_llm_provider in litellm.openai_compatible_providers: + _, custom_llm_provider, api_key, api_base = litellm.get_llm_provider( + model=model_name + ) + default_api_base = api_base + default_api_key = api_key + + if ( + model_name in litellm.open_ai_chat_completion_models + or custom_llm_provider in litellm.openai_compatible_providers + or custom_llm_provider == "azure" + or custom_llm_provider == "azure_text" + or custom_llm_provider == "custom_openai" + or custom_llm_provider == "openai" + or custom_llm_provider == "text-completion-openai" + or "ft:gpt-3.5-turbo" in model_name + or model_name in litellm.open_ai_embedding_models + ): + is_azure_ai_studio_model: bool = False + if custom_llm_provider == "azure": + if litellm.utils._is_non_openai_azure_model(model_name): + is_azure_ai_studio_model = True + custom_llm_provider = "openai" + # remove azure prefx from model_name + model_name = model_name.replace("azure/", "") + # glorified / complicated reading of configs + # user can pass vars directly or they can pas os.environ/AZURE_API_KEY, in which case we will read the env + # we do this here because we init clients for Azure, OpenAI and we need to set the right key + api_key = litellm_params.get("api_key") or default_api_key + if ( + api_key + and isinstance(api_key, str) + and api_key.startswith("os.environ/") ): - try: - azure_ad_token_provider = get_azure_ad_token_provider() - except ValueError: - verbose_router_logger.debug( - "Azure AD Token Provider could not be used." - ) - if api_version is None: - api_version = os.getenv( - "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION - ) + api_key_env_name = api_key.replace("os.environ/", "") + api_key = get_secret_str(api_key_env_name) + litellm_params["api_key"] = api_key - if "gateway.ai.cloudflare.com" in api_base: - if not api_base.endswith("/"): + api_base = litellm_params.get("api_base") + base_url: Optional[str] = litellm_params.get("base_url") + api_base = ( + api_base or base_url or default_api_base + ) # allow users to pass in `api_base` or `base_url` for azure + if api_base and api_base.startswith("os.environ/"): + api_base_env_name = api_base.replace("os.environ/", "") + api_base = get_secret_str(api_base_env_name) + litellm_params["api_base"] = api_base + + ## AZURE AI STUDIO MISTRAL CHECK ## + """ + Make sure api base ends in /v1/ + + if not, add it - https://github.com/BerriAI/litellm/issues/2279 + """ + if ( + is_azure_ai_studio_model is True + and api_base is not None + and isinstance(api_base, str) + and not api_base.endswith("/v1/") + ): + # check if it ends with a trailing slash + if api_base.endswith("/"): + api_base += "v1/" + elif api_base.endswith("/v1"): api_base += "/" - azure_model = model_name.replace("azure/", "") - api_base += f"{azure_model}" - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + else: + api_base += "/v1/" - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance + api_version = litellm_params.get("api_version") + if api_version and api_version.startswith("os.environ/"): + api_version_env_name = api_version.replace("os.environ/", "") + api_version = get_secret_str(api_version_env_name) + litellm_params["api_version"] = api_version + + timeout: Optional[float] = ( + litellm_params.pop("timeout", None) or litellm.request_timeout + ) + if isinstance(timeout, str) and timeout.startswith("os.environ/"): + timeout_env_name = timeout.replace("os.environ/", "") + timeout = get_secret(timeout_env_name) # type: ignore + litellm_params["timeout"] = timeout + + stream_timeout: Optional[float] = litellm_params.pop( + "stream_timeout", timeout + ) # if no stream_timeout is set, default to timeout + if isinstance(stream_timeout, str) and stream_timeout.startswith( + "os.environ/" + ): + stream_timeout_env_name = stream_timeout.replace("os.environ/", "") + stream_timeout = get_secret(stream_timeout_env_name) # type: ignore + litellm_params["stream_timeout"] = stream_timeout + + max_retries: Optional[int] = litellm_params.pop( + "max_retries", 0 + ) # router handles retry logic + if isinstance(max_retries, str) and max_retries.startswith("os.environ/"): + max_retries_env_name = max_retries.replace("os.environ/", "") + max_retries = get_secret(max_retries_env_name) # type: ignore + litellm_params["max_retries"] = max_retries + + organization = litellm_params.get("organization", None) + if isinstance(organization, str) and organization.startswith("os.environ/"): + organization_env_name = organization.replace("os.environ/", "") + organization = get_secret_str(organization_env_name) + litellm_params["organization"] = organization + azure_ad_token_provider: Optional[Callable[[], str]] = None + if litellm_params.get("tenant_id"): + verbose_router_logger.debug( + "Using Azure AD Token Provider for Azure Auth" + ) + azure_ad_token_provider = ( + InitalizeOpenAISDKClient.get_azure_ad_token_from_entrata_id( + tenant_id=litellm_params.get("tenant_id"), + client_id=litellm_params.get("client_id"), + client_secret=litellm_params.get("client_secret"), + ) + ) + + if custom_llm_provider == "azure" or custom_llm_provider == "azure_text": + if api_base is None or not isinstance(api_base, str): + filtered_litellm_params = { + k: v + for k, v in model["litellm_params"].items() + if k != "api_key" + } + _filtered_model = { + "model_name": model["model_name"], + "litellm_params": filtered_litellm_params, + } + raise ValueError( + f"api_base is required for Azure OpenAI. Set it on your config. Model - {_filtered_model}" + ) + azure_ad_token = litellm_params.get("azure_ad_token") + if azure_ad_token is not None: + if azure_ad_token.startswith("oidc/"): + azure_ad_token = get_azure_ad_token_from_oidc(azure_ad_token) + elif ( + azure_ad_token_provider is None + and litellm.enable_azure_ad_token_refresh is True ): - cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore + try: + azure_ad_token_provider = get_azure_ad_token_provider() + except ValueError: + verbose_router_logger.debug( + "Azure AD Token Provider could not be used." + ) + if api_version is None: + api_version = os.getenv( + "AZURE_API_VERSION", litellm.AZURE_DEFAULT_API_VERSION + ) + + if "gateway.ai.cloudflare.com" in api_base: + if not api_base.endswith("/"): + api_base += "/" + azure_model = model_name.replace("azure/", "") + api_base += f"{azure_model}" + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( api_key=api_key, azure_ad_token=azure_ad_token, azure_ad_token_provider=azure_ad_token_provider, @@ -260,7 +251,7 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL api_version=api_version, timeout=timeout, # type: ignore max_retries=max_retries, # type: ignore - http_client=httpx.Client( + http_client=httpx.AsyncClient( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 ), @@ -273,35 +264,35 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL ttl=client_ttl, local_only=True, ) # cache for 1 hr - # streaming clients can have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - api_key=api_key, - azure_ad_token=azure_ad_token, - azure_ad_token_provider=azure_ad_token_provider, - base_url=api_base, - api_version=api_version, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + # streaming clients can have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore api_key=api_key, azure_ad_token=azure_ad_token, azure_ad_token_provider=azure_ad_token_provider, @@ -309,7 +300,7 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL api_version=api_version, timeout=stream_timeout, # type: ignore max_retries=max_retries, # type: ignore - http_client=httpx.Client( + http_client=httpx.AsyncClient( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 ), @@ -322,41 +313,159 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL ttl=client_ttl, local_only=True, ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + api_key=api_key, + azure_ad_token=azure_ad_token, + azure_ad_token_provider=azure_ad_token_provider, + base_url=api_base, + api_version=api_version, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + else: + _api_key = api_key + if _api_key is not None and isinstance(_api_key, str): + # only show first 5 chars of api_key + _api_key = _api_key[:8] + "*" * 15 + verbose_router_logger.debug( + f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" + ) + azure_client_params = { + "api_key": api_key, + "azure_endpoint": api_base, + "api_version": api_version, + "azure_ad_token": azure_ad_token, + "azure_ad_token_provider": azure_ad_token_provider, + } + + if azure_ad_token_provider is not None: + azure_client_params["azure_ad_token_provider"] = ( + azure_ad_token_provider + ) + from litellm.llms.AzureOpenAI.azure import ( + select_azure_base_url_or_endpoint, + ) + + # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client + # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client + azure_client_params = select_azure_base_url_or_endpoint( + azure_client_params + ) + + cache_key = f"{model_id}_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), # type: ignore + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + # streaming clients should have diff timeouts + cache_key = f"{model_id}_stream_async_client" + _client = openai.AsyncAzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.AsyncClient( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + + if InitalizeOpenAISDKClient.should_initialize_sync_client( + litellm_router_instance=litellm_router_instance + ): + cache_key = f"{model_id}_stream_client" + _client = openai.AzureOpenAI( # type: ignore + **azure_client_params, + timeout=stream_timeout, # type: ignore + max_retries=max_retries, # type: ignore + http_client=httpx.Client( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, + ), + ) + litellm_router_instance.cache.set_cache( + key=cache_key, + value=_client, + ttl=client_ttl, + local_only=True, + ) # cache for 1 hr + else: - _api_key = api_key + _api_key = api_key # type: ignore if _api_key is not None and isinstance(_api_key, str): # only show first 5 chars of api_key _api_key = _api_key[:8] + "*" * 15 verbose_router_logger.debug( - f"Initializing Azure OpenAI Client for {model_name}, Api Base: {str(api_base)}, Api Key:{_api_key}" + f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" ) - azure_client_params = { - "api_key": api_key, - "azure_endpoint": api_base, - "api_version": api_version, - "azure_ad_token": azure_ad_token, - "azure_ad_token_provider": azure_ad_token_provider, - } - - if azure_ad_token_provider is not None: - azure_client_params["azure_ad_token_provider"] = ( - azure_ad_token_provider - ) - from litellm.llms.AzureOpenAI.azure import ( - select_azure_base_url_or_endpoint, - ) - - # this decides if we should set azure_endpoint or base_url on Azure OpenAI Client - # required to support GPT-4 vision enhancements, since base_url needs to be set on Azure OpenAI Client - azure_client_params = select_azure_base_url_or_endpoint( - azure_client_params - ) - cache_key = f"{model_id}_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, timeout=timeout, # type: ignore max_retries=max_retries, # type: ignore + organization=organization, http_client=httpx.AsyncClient( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 @@ -370,14 +479,17 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL ttl=client_ttl, local_only=True, ) # cache for 1 hr - if should_initialize_sync_client( + + if InitalizeOpenAISDKClient.should_initialize_sync_client( litellm_router_instance=litellm_router_instance ): cache_key = f"{model_id}_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, timeout=timeout, # type: ignore max_retries=max_retries, # type: ignore + organization=organization, http_client=httpx.Client( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 @@ -394,16 +506,18 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL # streaming clients should have diff timeouts cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncAzureOpenAI( # type: ignore - **azure_client_params, + _client = openai.AsyncOpenAI( # type: ignore + api_key=api_key, + base_url=api_base, timeout=stream_timeout, # type: ignore max_retries=max_retries, # type: ignore + organization=organization, http_client=httpx.AsyncClient( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 ), verify=litellm.ssl_verify, - ), + ), # type: ignore ) litellm_router_instance.cache.set_cache( key=cache_key, @@ -412,20 +526,23 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL local_only=True, ) # cache for 1 hr - if should_initialize_sync_client( + if InitalizeOpenAISDKClient.should_initialize_sync_client( litellm_router_instance=litellm_router_instance ): + # streaming clients should have diff timeouts cache_key = f"{model_id}_stream_client" - _client = openai.AzureOpenAI( # type: ignore - **azure_client_params, + _client = openai.OpenAI( # type: ignore + api_key=api_key, + base_url=api_base, timeout=stream_timeout, # type: ignore max_retries=max_retries, # type: ignore + organization=organization, http_client=httpx.Client( limits=httpx.Limits( max_connections=1000, max_keepalive_connections=100 ), verify=litellm.ssl_verify, - ), + ), # type: ignore ) litellm_router_instance.cache.set_cache( key=cache_key, @@ -434,149 +551,49 @@ def set_client(litellm_router_instance: LitellmRouter, model: dict): # noqa: PL local_only=True, ) # cache for 1 hr + @staticmethod + def get_azure_ad_token_from_entrata_id( + tenant_id: str, client_id: str, client_secret: str + ) -> Callable[[], str]: + from azure.identity import ( + ClientSecretCredential, + DefaultAzureCredential, + get_bearer_token_provider, + ) + + verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") + + if tenant_id.startswith("os.environ/"): + _tenant_id = get_secret_str(tenant_id) else: - _api_key = api_key # type: ignore - if _api_key is not None and isinstance(_api_key, str): - # only show first 5 chars of api_key - _api_key = _api_key[:8] + "*" * 15 - verbose_router_logger.debug( - f"Initializing OpenAI Client for {model_name}, Api Base:{str(api_base)}, Api Key:{_api_key}" - ) - cache_key = f"{model_id}_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + _tenant_id = tenant_id - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - cache_key = f"{model_id}_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + if client_id.startswith("os.environ/"): + _client_id = get_secret_str(client_id) + else: + _client_id = client_id - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_async_client" - _client = openai.AsyncOpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.AsyncClient( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + if client_secret.startswith("os.environ/"): + _client_secret = get_secret_str(client_secret) + else: + _client_secret = client_secret - if should_initialize_sync_client( - litellm_router_instance=litellm_router_instance - ): - # streaming clients should have diff timeouts - cache_key = f"{model_id}_stream_client" - _client = openai.OpenAI( # type: ignore - api_key=api_key, - base_url=api_base, - timeout=stream_timeout, # type: ignore - max_retries=max_retries, # type: ignore - organization=organization, - http_client=httpx.Client( - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 - ), - verify=litellm.ssl_verify, - ), # type: ignore - ) - litellm_router_instance.cache.set_cache( - key=cache_key, - value=_client, - ttl=client_ttl, - local_only=True, - ) # cache for 1 hr + verbose_router_logger.debug( + "tenant_id %s, client_id %s, client_secret %s", + _tenant_id, + _client_id, + _client_secret, + ) + if _tenant_id is None or _client_id is None or _client_secret is None: + raise ValueError("tenant_id, client_id, and client_secret must be provided") + credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) + verbose_router_logger.debug("credential %s", credential) -def get_azure_ad_token_from_entrata_id( - tenant_id: str, client_id: str, client_secret: str -) -> Callable[[], str]: - from azure.identity import ( - ClientSecretCredential, - DefaultAzureCredential, - get_bearer_token_provider, - ) + token_provider = get_bearer_token_provider( + credential, "https://cognitiveservices.azure.com/.default" + ) - verbose_router_logger.debug("Getting Azure AD Token from Entrata ID") + verbose_router_logger.debug("token_provider %s", token_provider) - if tenant_id.startswith("os.environ/"): - _tenant_id = get_secret_str(tenant_id) - else: - _tenant_id = tenant_id - - if client_id.startswith("os.environ/"): - _client_id = get_secret_str(client_id) - else: - _client_id = client_id - - if client_secret.startswith("os.environ/"): - _client_secret = get_secret_str(client_secret) - else: - _client_secret = client_secret - - verbose_router_logger.debug( - "tenant_id %s, client_id %s, client_secret %s", - _tenant_id, - _client_id, - _client_secret, - ) - if _tenant_id is None or _client_id is None or _client_secret is None: - raise ValueError("tenant_id, client_id, and client_secret must be provided") - credential = ClientSecretCredential(_tenant_id, _client_id, _client_secret) - - verbose_router_logger.debug("credential %s", credential) - - token_provider = get_bearer_token_provider( - credential, "https://cognitiveservices.azure.com/.default" - ) - - verbose_router_logger.debug("token_provider %s", token_provider) - - return token_provider + return token_provider From c731ba4044b45007619ea2c7d3dbad8c84323765 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 24 Oct 2024 19:27:50 +0400 Subject: [PATCH 27/62] (code cleanup) remove unused and undocumented logging integrations - litedebugger, berrispend (#6406) * code cleanup remove unused and undocumented code files * fix unused logging integrations cleanup --- litellm/integrations/aispend.py | 167 ------------------ litellm/integrations/berrispend.py | 86 --------- litellm/integrations/litedebugger.py | 77 -------- litellm/litellm_core_utils/litellm_logging.py | 7 +- 4 files changed, 1 insertion(+), 336 deletions(-) delete mode 100644 litellm/integrations/aispend.py delete mode 100644 litellm/integrations/berrispend.py delete mode 100644 litellm/integrations/litedebugger.py diff --git a/litellm/integrations/aispend.py b/litellm/integrations/aispend.py deleted file mode 100644 index 3a62c646f3..0000000000 --- a/litellm/integrations/aispend.py +++ /dev/null @@ -1,167 +0,0 @@ -#### What this does #### -# On success + failure, log events to aispend.io -import datetime -import os -import traceback - -import dotenv - -model_cost = { - "gpt-3.5-turbo": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-35-turbo": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, # azure model name - "gpt-3.5-turbo-0613": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-3.5-turbo-0301": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-3.5-turbo-16k": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, - "gpt-35-turbo-16k": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, # azure model name - "gpt-3.5-turbo-16k-0613": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, - "gpt-4": { - "max_tokens": 8000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.00006, - }, - "gpt-4-0613": { - "max_tokens": 8000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.00006, - }, - "gpt-4-32k": { - "max_tokens": 8000, - "input_cost_per_token": 0.00006, - "output_cost_per_token": 0.00012, - }, - "claude-instant-1": { - "max_tokens": 100000, - "input_cost_per_token": 0.00000163, - "output_cost_per_token": 0.00000551, - }, - "claude-2": { - "max_tokens": 100000, - "input_cost_per_token": 0.00001102, - "output_cost_per_token": 0.00003268, - }, - "text-bison-001": { - "max_tokens": 8192, - "input_cost_per_token": 0.000004, - "output_cost_per_token": 0.000004, - }, - "chat-bison-001": { - "max_tokens": 4096, - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000002, - }, - "command-nightly": { - "max_tokens": 4096, - "input_cost_per_token": 0.000015, - "output_cost_per_token": 0.000015, - }, -} - - -class AISpendLogger: - # Class variables or attributes - def __init__(self): - # Instance variables - self.account_id = os.getenv("AISPEND_ACCOUNT_ID") - self.api_key = os.getenv("AISPEND_API_KEY") - - def price_calculator(self, model, response_obj, start_time, end_time): - # try and find if the model is in the model_cost map - # else default to the average of the costs - prompt_tokens_cost_usd_dollar = 0 - completion_tokens_cost_usd_dollar = 0 - if model in model_cost: - prompt_tokens_cost_usd_dollar = ( - model_cost[model]["input_cost_per_token"] - * response_obj["usage"]["prompt_tokens"] - ) - completion_tokens_cost_usd_dollar = ( - model_cost[model]["output_cost_per_token"] - * response_obj["usage"]["completion_tokens"] - ) - elif "replicate" in model: - # replicate models are charged based on time - # llama 2 runs on an nvidia a100 which costs $0.0032 per second - https://replicate.com/replicate/llama-2-70b-chat - model_run_time = end_time - start_time # assuming time in seconds - cost_usd_dollar = model_run_time * 0.0032 - prompt_tokens_cost_usd_dollar = cost_usd_dollar / 2 - completion_tokens_cost_usd_dollar = cost_usd_dollar / 2 - else: - # calculate average input cost - input_cost_sum = 0 - output_cost_sum = 0 - for model in model_cost: - input_cost_sum += model_cost[model]["input_cost_per_token"] - output_cost_sum += model_cost[model]["output_cost_per_token"] - prompt_tokens_cost_usd_dollar = ( - model_cost[model]["input_cost_per_token"] - * response_obj["usage"]["prompt_tokens"] - ) - completion_tokens_cost_usd_dollar = ( - model_cost[model]["output_cost_per_token"] - * response_obj["usage"]["completion_tokens"] - ) - return prompt_tokens_cost_usd_dollar, completion_tokens_cost_usd_dollar - - def log_event(self, model, response_obj, start_time, end_time, print_verbose): - # Method definition - try: - print_verbose( - f"AISpend Logging - Enters logging function for model {model}" - ) - - response_timestamp = datetime.datetime.fromtimestamp( - int(response_obj["created"]) - ).strftime("%Y-%m-%d") - - ( - prompt_tokens_cost_usd_dollar, - completion_tokens_cost_usd_dollar, - ) = self.price_calculator(model, response_obj, start_time, end_time) - prompt_tokens_cost_usd_cent = prompt_tokens_cost_usd_dollar * 100 - completion_tokens_cost_usd_cent = completion_tokens_cost_usd_dollar * 100 - data = [ - { - "requests": 1, - "requests_context": 1, - "context_tokens": response_obj["usage"]["prompt_tokens"], - "requests_generated": 1, - "generated_tokens": response_obj["usage"]["completion_tokens"], - "recorded_date": response_timestamp, - "model_id": response_obj["model"], - "generated_tokens_cost_usd_cent": prompt_tokens_cost_usd_cent, - "context_tokens_cost_usd_cent": completion_tokens_cost_usd_cent, - } - ] - - print_verbose(f"AISpend Logging - final data object: {data}") - except Exception: - print_verbose(f"AISpend Logging Error - {traceback.format_exc()}") - pass diff --git a/litellm/integrations/berrispend.py b/litellm/integrations/berrispend.py deleted file mode 100644 index a1ad2e6a24..0000000000 --- a/litellm/integrations/berrispend.py +++ /dev/null @@ -1,86 +0,0 @@ -#### What this does #### -# On success + failure, log events to aispend.io -import datetime -import os -import traceback - -import dotenv -import requests # type: ignore - -model_cost = { - "gpt-3.5-turbo": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-35-turbo": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, # azure model name - "gpt-3.5-turbo-0613": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-3.5-turbo-0301": { - "max_tokens": 4000, - "input_cost_per_token": 0.0000015, - "output_cost_per_token": 0.000002, - }, - "gpt-3.5-turbo-16k": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, - "gpt-35-turbo-16k": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, # azure model name - "gpt-3.5-turbo-16k-0613": { - "max_tokens": 16000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.000004, - }, - "gpt-4": { - "max_tokens": 8000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.00006, - }, - "gpt-4-0613": { - "max_tokens": 8000, - "input_cost_per_token": 0.000003, - "output_cost_per_token": 0.00006, - }, - "gpt-4-32k": { - "max_tokens": 8000, - "input_cost_per_token": 0.00006, - "output_cost_per_token": 0.00012, - }, - "claude-instant-1": { - "max_tokens": 100000, - "input_cost_per_token": 0.00000163, - "output_cost_per_token": 0.00000551, - }, - "claude-2": { - "max_tokens": 100000, - "input_cost_per_token": 0.00001102, - "output_cost_per_token": 0.00003268, - }, - "text-bison-001": { - "max_tokens": 8192, - "input_cost_per_token": 0.000004, - "output_cost_per_token": 0.000004, - }, - "chat-bison-001": { - "max_tokens": 4096, - "input_cost_per_token": 0.000002, - "output_cost_per_token": 0.000002, - }, - "command-nightly": { - "max_tokens": 4096, - "input_cost_per_token": 0.000015, - "output_cost_per_token": 0.000015, - }, -} diff --git a/litellm/integrations/litedebugger.py b/litellm/integrations/litedebugger.py deleted file mode 100644 index 6af8810bf6..0000000000 --- a/litellm/integrations/litedebugger.py +++ /dev/null @@ -1,77 +0,0 @@ -import json -import os -import traceback -import types - -import requests - - -class LiteDebugger: - user_email = None - dashboard_url = None - - def __init__(self, email=None): - self.api_url = "https://api.litellm.ai/debugger" - self.validate_environment(email) - pass - - def validate_environment(self, email): - try: - self.user_email = ( - email or os.getenv("LITELLM_TOKEN") or os.getenv("LITELLM_EMAIL") - ) - if ( - self.user_email is None - ): # if users are trying to use_client=True but token not set - raise ValueError( - "litellm.use_client = True but no token or email passed. Please set it in litellm.token" - ) - self.dashboard_url = "https://admin.litellm.ai/" + self.user_email - if self.user_email is None: - raise ValueError( - "[Non-Blocking Error] LiteLLMDebugger: Missing LITELLM_TOKEN. Set it in your environment. Eg.: os.environ['LITELLM_TOKEN']= " - ) - except Exception: - raise ValueError( - "[Non-Blocking Error] LiteLLMDebugger: Missing LITELLM_TOKEN. Set it in your environment. Eg.: os.environ['LITELLM_TOKEN']= " - ) - - def input_log_event( - self, - model, - messages, - end_user, - litellm_call_id, - call_type, - print_verbose, - litellm_params, - optional_params, - ): - """ - This integration is not implemented yet. - """ - return - - def post_call_log_event( - self, original_response, litellm_call_id, print_verbose, call_type, stream - ): - """ - This integration is not implemented yet. - """ - return - - def log_event( - self, - end_user, - response_obj, - start_time, - end_time, - litellm_call_id, - print_verbose, - call_type, - stream=False, - ): - """ - This integration is not implemented yet. - """ - return diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 8a80f9b963..0a298d33b8 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -58,7 +58,6 @@ from litellm.utils import ( prompt_token_calculator, ) -from ..integrations.aispend import AISpendLogger from ..integrations.argilla import ArgillaLogger from ..integrations.arize_ai import ArizeLogger from ..integrations.athina import AthinaLogger @@ -73,7 +72,6 @@ from ..integrations.lago import LagoLogger from ..integrations.langfuse.langfuse import LangFuseLogger from ..integrations.langfuse.langfuse_handler import LangFuseHandler from ..integrations.langsmith import LangsmithLogger -from ..integrations.litedebugger import LiteDebugger from ..integrations.literal_ai import LiteralAILogger from ..integrations.logfire_logger import LogfireLevel, LogfireLogger from ..integrations.lunary import LunaryLogger @@ -124,7 +122,6 @@ s3Logger = None genericAPILogger = None greenscaleLogger = None lunaryLogger = None -aispendLogger = None supabaseClient = None callback_list: Optional[List[str]] = [] user_logger_fn = None @@ -2044,7 +2041,7 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915 """ Globally sets the callback client """ - global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, aispendLogger, supabaseClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger + global sentry_sdk_instance, capture_exception, add_breadcrumb, posthog, slack_app, alerts_channel, traceloopLogger, athinaLogger, heliconeLogger, supabaseClient, lunaryLogger, promptLayerLogger, langFuseLogger, customLogger, weightsBiasesLogger, logfireLogger, dynamoLogger, s3Logger, dataDogLogger, prometheusLogger, greenscaleLogger, openMeterLogger try: for callback in callback_list: @@ -2124,8 +2121,6 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915 weightsBiasesLogger = WeightsBiasesLogger() elif callback == "logfire": logfireLogger = LogfireLogger() - elif callback == "aispend": - aispendLogger = AISpendLogger() elif callback == "supabase": print_verbose("instantiating supabase") supabaseClient = Supabase() From 0f0470f5749e5169cb981469f21f7c4c0a584940 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 24 Oct 2024 21:29:48 +0400 Subject: [PATCH 28/62] =?UTF-8?q?bump:=20version=201.50.3=20=E2=86=92=201.?= =?UTF-8?q?50.4?= 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 9122eac93f..68b5f78bda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.50.3" +version = "1.50.4" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -91,7 +91,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.50.3" +version = "1.50.4" version_files = [ "pyproject.toml:^version" ] From d59f8f952d87d1b57d0275d18e974610203b5201 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 17:48:36 -0700 Subject: [PATCH 29/62] perf: remove 'always_read_redis' - adding +830ms on each llm call (#6414) * perf: remove 'always_read_redis' - adding +830ms on each llm call * test: cleanup codestral tests - backend api unavailable --- litellm/__init__.py | 3 -- litellm/caching/dual_cache.py | 10 ++---- litellm/integrations/opentelemetry.py | 2 +- litellm/proxy/_new_secret_config.yaml | 51 ++++++++++++++++++++++++--- tests/local_testing/test_caching.py | 40 --------------------- 5 files changed, 49 insertions(+), 57 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 2aa89a03c8..11b34f5048 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -160,9 +160,6 @@ enable_caching_on_provider_specific_optional_params: bool = ( caching: bool = ( False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 ) -always_read_redis: bool = ( - True # always use redis for rate limiting logic on litellm proxy -) caching_with_models: bool = ( False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 ) diff --git a/litellm/caching/dual_cache.py b/litellm/caching/dual_cache.py index 720da9ad63..ec6a6c1638 100644 --- a/litellm/caching/dual_cache.py +++ b/litellm/caching/dual_cache.py @@ -32,7 +32,6 @@ class DualCache(BaseCache): redis_cache: Optional[RedisCache] = None, default_in_memory_ttl: Optional[float] = None, default_redis_ttl: Optional[float] = None, - always_read_redis: Optional[bool] = True, ) -> None: super().__init__() # If in_memory_cache is not provided, use the default InMemoryCache @@ -44,7 +43,6 @@ class DualCache(BaseCache): default_in_memory_ttl or litellm.default_in_memory_ttl ) self.default_redis_ttl = default_redis_ttl or litellm.default_redis_ttl - self.always_read_redis = always_read_redis def update_cache_ttl( self, default_in_memory_ttl: Optional[float], default_redis_ttl: Optional[float] @@ -102,12 +100,8 @@ class DualCache(BaseCache): if in_memory_result is not None: result = in_memory_result - if ( - (self.always_read_redis is True) - and self.redis_cache is not None - and local_only is False - ): - # If not found in in-memory cache or always_read_redis is True, try fetching from Redis + if result is None and self.redis_cache is not None and local_only is False: + # If not found in in-memory cache, try fetching from Redis redis_result = self.redis_cache.get_cache(key, **kwargs) if redis_result is not None: diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 8ba871acc8..f1b7abbbbf 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -171,7 +171,7 @@ class OpenTelemetry(CustomLogger): try: value = str(value) except Exception: - value = "litllm logging error - could_not_json_serialize" + value = "litellm logging error - could_not_json_serialize" self.safe_set_attribute( span=service_logging_span, key=key, diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 00f4da8d91..2cdf35b705 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -1,10 +1,51 @@ model_list: - model_name: gpt-4o litellm_params: - model: azure/gpt-4o-realtime-preview - api_key: os.environ/AZURE_SWEDEN_API_KEY - api_base: os.environ/AZURE_SWEDEN_API_BASE + model: openai/fake + api_key: fake-key + api_base: https://exampleopenaiendpoint-production.up.railway.app/ litellm_settings: - success_callback: ["langfuse"] - # logged_real_time_event_types: "*" \ No newline at end of file + callbacks: ["prometheus", "otel"] + +general_settings: + user_api_key_cache_ttl: 3600 + +router_settings: + routing_strategy: latency-based-routing + routing_strategy_args: + # only assign 40% of traffic to the fastest deployment to avoid overloading it + lowest_latency_buffer: 0.4 + + # consider last five minutes of calls for latency calculation + ttl: 300 + + # model_group_alias: + # gpt-4o: gpt-4o-128k-2024-05-13 + # gpt-4o-mini: gpt-4o-mini-128k-2024-07-18 + + enable_tag_filtering: True + + # retry call 3 times on each model_name (we don't use fallbacks, so this would be 3 times total) + num_retries: 3 + + # -- cooldown settings -- + # see https://github.com/BerriAI/litellm/blob/main/litellm/router_utils/cooldown_handlers.py#L265 + + # cooldown model if it fails > n calls in a minute. + allowed_fails: 2 + + # (in seconds) how long to cooldown model if fails/min > allowed_fails + cooldown_time: 60 + + allowed_fails_policy: + InternalServerErrorAllowedFails: 1 + RateLimitErrorAllowedFails: 2 + TimeoutErrorAllowedFails: 3 + # -- end cooldown settings -- + + # see https://docs.litellm.ai/docs/proxy/prod#3-use-redis-porthost-password-not-redis_url + redis_host: os.environ/REDIS_HOST + redis_port: os.environ/REDIS_PORT + redis_password: os.environ/REDIS_PASSWORD + diff --git a/tests/local_testing/test_caching.py b/tests/local_testing/test_caching.py index dfadf11bbe..f56079aa7e 100644 --- a/tests/local_testing/test_caching.py +++ b/tests/local_testing/test_caching.py @@ -2066,46 +2066,6 @@ async def test_cache_default_off_acompletion(): assert response3.id == response4.id -@pytest.mark.asyncio() -@pytest.mark.skip(reason="dual caching should first prioritze local cache") -async def test_dual_cache_uses_redis(): - """ - - - Store diff values in redis and in memory cache - - call get cache - - Assert that value from redis is used - """ - litellm.set_verbose = True - from litellm.caching.caching import DualCache, RedisCache - - current_usage = uuid.uuid4() - - _cache_obj = DualCache(redis_cache=RedisCache(), always_read_redis=True) - - # set cache - await _cache_obj.async_set_cache(key=f"current_usage: {current_usage}", value=10) - - # modify value of in memory cache - _cache_obj.in_memory_cache.cache_dict[f"current_usage: {current_usage}"] = 1 - - # get cache - value = await _cache_obj.async_get_cache(key=f"current_usage: {current_usage}") - print("value from dual cache", value) - assert value == 10 - - -@pytest.mark.asyncio() -async def test_proxy_logging_setup(): - """ - Assert always_read_redis is True when used by internal usage cache - """ - from litellm.caching.caching import DualCache - from litellm.proxy.utils import ProxyLogging - - pl_obj = ProxyLogging(user_api_key_cache=DualCache()) - assert pl_obj.internal_usage_cache.dual_cache.always_read_redis is True - - @pytest.mark.skip(reason="local test. Requires sentinel setup.") @pytest.mark.asyncio async def test_redis_sentinel_caching(): From c04c4a82f1bdf27af21a9e80fe7f088129926367 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 18:59:01 -0700 Subject: [PATCH 30/62] =?UTF-8?q?feat(litellm=5Flogging.py):=20refactor=20?= =?UTF-8?q?standard=5Flogging=5Fpayload=20function=20=E2=80=A6=20(#6388)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(litellm_logging.py): refactor standard_logging_payload function to be <50 LOC fixes issue where usage information was not following typed values * fix(litellm_logging.py): fix completion start time handling --- litellm/integrations/prometheus.py | 24 +- litellm/litellm_core_utils/litellm_logging.py | 316 +++++++++++------- .../test_standard_logging_payload.py | 67 ++++ 3 files changed, 286 insertions(+), 121 deletions(-) create mode 100644 tests/logging_callback_tests/test_standard_logging_payload.py diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index 8a4f409b6c..bf19c364ef 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -346,8 +346,12 @@ class PrometheusLogger(CustomLogger): standard_logging_payload: Optional[StandardLoggingPayload] = kwargs.get( "standard_logging_object" ) - if standard_logging_payload is None: - raise ValueError("standard_logging_object is required") + if standard_logging_payload is None or not isinstance( + standard_logging_payload, dict + ): + raise ValueError( + f"standard_logging_object is required, got={standard_logging_payload}" + ) model = kwargs.get("model", "") litellm_params = kwargs.get("litellm_params", {}) or {} _metadata = litellm_params.get("metadata", {}) @@ -991,7 +995,7 @@ class PrometheusLogger(CustomLogger): """ from litellm.litellm_core_utils.litellm_logging import ( StandardLoggingMetadata, - get_standard_logging_metadata, + StandardLoggingPayloadSetup, ) verbose_logger.debug( @@ -1000,8 +1004,10 @@ class PrometheusLogger(CustomLogger): kwargs, ) _metadata = kwargs.get("metadata", {}) - standard_metadata: StandardLoggingMetadata = get_standard_logging_metadata( - metadata=_metadata + standard_metadata: StandardLoggingMetadata = ( + StandardLoggingPayloadSetup.get_standard_logging_metadata( + metadata=_metadata + ) ) _new_model = kwargs.get("model") self.litellm_deployment_successful_fallbacks.labels( @@ -1023,7 +1029,7 @@ class PrometheusLogger(CustomLogger): """ from litellm.litellm_core_utils.litellm_logging import ( StandardLoggingMetadata, - get_standard_logging_metadata, + StandardLoggingPayloadSetup, ) verbose_logger.debug( @@ -1033,8 +1039,10 @@ class PrometheusLogger(CustomLogger): ) _new_model = kwargs.get("model") _metadata = kwargs.get("metadata", {}) - standard_metadata: StandardLoggingMetadata = get_standard_logging_metadata( - metadata=_metadata + standard_metadata: StandardLoggingMetadata = ( + StandardLoggingPayloadSetup.get_standard_logging_metadata( + metadata=_metadata + ) ) self.litellm_deployment_failed_fallbacks.labels( requested_model=original_model_group, diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 0a298d33b8..fd73352014 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -12,7 +12,7 @@ import time import traceback import uuid from datetime import datetime as dt_object -from typing import Any, Callable, Dict, List, Literal, Optional, Union +from typing import Any, Callable, Dict, List, Literal, Optional, Tuple, Union from pydantic import BaseModel @@ -51,6 +51,7 @@ from litellm.types.utils import ( StandardPassThroughResponseObject, TextCompletionResponse, TranscriptionResponse, + Usage, ) from litellm.utils import ( _get_base_model_from_metadata, @@ -2454,7 +2455,183 @@ def is_valid_sha256_hash(value: str) -> bool: return bool(re.fullmatch(r"[a-fA-F0-9]{64}", value)) -def get_standard_logging_object_payload( # noqa: PLR0915 +class StandardLoggingPayloadSetup: + @staticmethod + def cleanup_timestamps( + start_time: Union[dt_object, float], + end_time: Union[dt_object, float], + completion_start_time: Union[dt_object, float], + ) -> Tuple[float, float, float]: + """ + Convert datetime objects to floats + """ + + if isinstance(start_time, datetime.datetime): + start_time_float = start_time.timestamp() + elif isinstance(start_time, float): + start_time_float = start_time + else: + raise ValueError( + f"start_time is required, got={start_time} of type {type(start_time)}" + ) + + if isinstance(end_time, datetime.datetime): + end_time_float = end_time.timestamp() + elif isinstance(end_time, float): + end_time_float = end_time + else: + raise ValueError( + f"end_time is required, got={end_time} of type {type(end_time)}" + ) + + if isinstance(completion_start_time, datetime.datetime): + completion_start_time_float = completion_start_time.timestamp() + elif isinstance(completion_start_time, float): + completion_start_time_float = completion_start_time + else: + completion_start_time_float = end_time_float + + return start_time_float, end_time_float, completion_start_time_float + + @staticmethod + def get_standard_logging_metadata( + metadata: Optional[Dict[str, Any]] + ) -> StandardLoggingMetadata: + """ + Clean and filter the metadata dictionary to include only the specified keys in StandardLoggingMetadata. + + Args: + metadata (Optional[Dict[str, Any]]): The original metadata dictionary. + + Returns: + StandardLoggingMetadata: A StandardLoggingMetadata object containing the cleaned metadata. + + Note: + - If the input metadata is None or not a dictionary, an empty StandardLoggingMetadata object is returned. + - If 'user_api_key' is present in metadata and is a valid SHA256 hash, it's stored as 'user_api_key_hash'. + """ + # Initialize with default values + clean_metadata = StandardLoggingMetadata( + user_api_key_hash=None, + user_api_key_alias=None, + user_api_key_team_id=None, + user_api_key_user_id=None, + user_api_key_team_alias=None, + spend_logs_metadata=None, + requester_ip_address=None, + requester_metadata=None, + ) + if isinstance(metadata, dict): + # Filter the metadata dictionary to include only the specified keys + clean_metadata = StandardLoggingMetadata( + **{ # type: ignore + key: metadata[key] + for key in StandardLoggingMetadata.__annotations__.keys() + if key in metadata + } + ) + + if metadata.get("user_api_key") is not None: + if is_valid_sha256_hash(str(metadata.get("user_api_key"))): + clean_metadata["user_api_key_hash"] = metadata.get( + "user_api_key" + ) # this is the hash + return clean_metadata + + @staticmethod + def get_usage_from_response_obj(response_obj: Optional[dict]) -> Usage: + ## BASE CASE ## + if response_obj is None: + return Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + ) + + usage = response_obj.get("usage", None) or {} + if usage is None or ( + not isinstance(usage, dict) and not isinstance(usage, Usage) + ): + return Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + ) + elif isinstance(usage, Usage): + return usage + elif isinstance(usage, dict): + return Usage(**usage) + + raise ValueError(f"usage is required, got={usage} of type {type(usage)}") + + @staticmethod + def get_model_cost_information( + base_model: Optional[str], + custom_pricing: Optional[bool], + custom_llm_provider: Optional[str], + init_response_obj: Union[Any, BaseModel, dict], + ) -> StandardLoggingModelInformation: + + model_cost_name = _select_model_name_for_cost_calc( + model=None, + completion_response=init_response_obj, # type: ignore + base_model=base_model, + custom_pricing=custom_pricing, + ) + if model_cost_name is None: + model_cost_information = StandardLoggingModelInformation( + model_map_key="", model_map_value=None + ) + else: + try: + _model_cost_information = litellm.get_model_info( + model=model_cost_name, custom_llm_provider=custom_llm_provider + ) + model_cost_information = StandardLoggingModelInformation( + model_map_key=model_cost_name, + model_map_value=_model_cost_information, + ) + except Exception: + verbose_logger.debug( # keep in debug otherwise it will trigger on every call + "Model={} is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload".format( + model_cost_name + ) + ) + model_cost_information = StandardLoggingModelInformation( + model_map_key=model_cost_name, model_map_value=None + ) + return model_cost_information + + @staticmethod + def get_final_response_obj( + response_obj: dict, init_response_obj: Union[Any, BaseModel, dict], kwargs: dict + ) -> Optional[Union[dict, str, list]]: + """ + Get final response object after redacting the message input/output from logging + """ + if response_obj is not None: + final_response_obj: Optional[Union[dict, str, list]] = response_obj + elif isinstance(init_response_obj, list) or isinstance(init_response_obj, str): + final_response_obj = init_response_obj + else: + final_response_obj = None + + modified_final_response_obj = redact_message_input_output_from_logging( + model_call_details=kwargs, + result=final_response_obj, + ) + + if modified_final_response_obj is not None and isinstance( + modified_final_response_obj, BaseModel + ): + final_response_obj = modified_final_response_obj.model_dump() + else: + final_response_obj = modified_final_response_obj + + return final_response_obj + + +def get_standard_logging_object_payload( kwargs: Optional[dict], init_response_obj: Union[Any, BaseModel, dict], start_time: dt_object, @@ -2502,9 +2679,9 @@ def get_standard_logging_object_payload( # noqa: PLR0915 completion_start_time = kwargs.get("completion_start_time", end_time) call_type = kwargs.get("call_type") cache_hit = kwargs.get("cache_hit", False) - usage = response_obj.get("usage", None) or {} - if type(usage) is litellm.Usage: - usage = dict(usage) + usage = StandardLoggingPayloadSetup.get_usage_from_response_obj( + response_obj=response_obj + ) id = response_obj.get("id", kwargs.get("litellm_call_id")) _model_id = metadata.get("model_info", {}).get("id", "") @@ -2517,20 +2694,13 @@ def get_standard_logging_object_payload( # noqa: PLR0915 ) # cleanup timestamps - if isinstance(start_time, datetime.datetime): - start_time_float = start_time.timestamp() - elif isinstance(start_time, float): - start_time_float = start_time - if isinstance(end_time, datetime.datetime): - end_time_float = end_time.timestamp() - elif isinstance(end_time, float): - end_time_float = end_time - if isinstance(completion_start_time, datetime.datetime): - completion_start_time_float = completion_start_time.timestamp() - elif isinstance(completion_start_time, float): - completion_start_time_float = completion_start_time - else: - completion_start_time_float = end_time_float + start_time_float, end_time_float, completion_start_time_float = ( + StandardLoggingPayloadSetup.cleanup_timestamps( + start_time=start_time, + end_time=end_time, + completion_start_time=completion_start_time, + ) + ) # clean up litellm hidden params clean_hidden_params = StandardLoggingHiddenParams( model_id=None, @@ -2548,7 +2718,9 @@ def get_standard_logging_object_payload( # noqa: PLR0915 } ) # clean up litellm metadata - clean_metadata = get_standard_logging_metadata(metadata=metadata) + clean_metadata = StandardLoggingPayloadSetup.get_standard_logging_metadata( + metadata=metadata + ) if litellm.cache is not None: cache_key = litellm.cache.get_cache_key(**kwargs) @@ -2570,58 +2742,21 @@ def get_standard_logging_object_payload( # noqa: PLR0915 ## Get model cost information ## base_model = _get_base_model_from_metadata(model_call_details=kwargs) custom_pricing = use_custom_pricing_for_model(litellm_params=litellm_params) - model_cost_name = _select_model_name_for_cost_calc( - model=None, - completion_response=init_response_obj, # type: ignore + model_cost_information = StandardLoggingPayloadSetup.get_model_cost_information( base_model=base_model, custom_pricing=custom_pricing, + custom_llm_provider=kwargs.get("custom_llm_provider"), + init_response_obj=init_response_obj, ) - if model_cost_name is None: - model_cost_information = StandardLoggingModelInformation( - model_map_key="", model_map_value=None - ) - else: - custom_llm_provider = kwargs.get("custom_llm_provider", None) - - try: - _model_cost_information = litellm.get_model_info( - model=model_cost_name, custom_llm_provider=custom_llm_provider - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, - model_map_value=_model_cost_information, - ) - except Exception: - verbose_logger.debug( # keep in debug otherwise it will trigger on every call - "Model={} is not mapped in model cost map. Defaulting to None model_cost_information for standard_logging_payload".format( - model_cost_name - ) - ) - model_cost_information = StandardLoggingModelInformation( - model_map_key=model_cost_name, model_map_value=None - ) - response_cost: float = kwargs.get("response_cost", 0) or 0.0 - if response_obj is not None: - final_response_obj: Optional[Union[dict, str, list]] = response_obj - elif isinstance(init_response_obj, list) or isinstance(init_response_obj, str): - final_response_obj = init_response_obj - else: - final_response_obj = None - - modified_final_response_obj = redact_message_input_output_from_logging( - model_call_details=kwargs, - result=final_response_obj, + ## get final response object ## + final_response_obj = StandardLoggingPayloadSetup.get_final_response_obj( + response_obj=response_obj, + init_response_obj=init_response_obj, + kwargs=kwargs, ) - if modified_final_response_obj is not None and isinstance( - modified_final_response_obj, BaseModel - ): - final_response_obj = modified_final_response_obj.model_dump() - else: - final_response_obj = modified_final_response_obj - payload: StandardLoggingPayload = StandardLoggingPayload( id=str(id), call_type=call_type or "", @@ -2635,9 +2770,9 @@ def get_standard_logging_object_payload( # noqa: PLR0915 metadata=clean_metadata, cache_key=cache_key, response_cost=response_cost, - total_tokens=usage.get("total_tokens", 0), - prompt_tokens=usage.get("prompt_tokens", 0), - completion_tokens=usage.get("completion_tokens", 0), + total_tokens=usage.total_tokens, + prompt_tokens=usage.prompt_tokens, + completion_tokens=usage.completion_tokens, request_tags=request_tags, end_user=end_user_id or "", api_base=litellm_params.get("api_base", ""), @@ -2663,51 +2798,6 @@ def get_standard_logging_object_payload( # noqa: PLR0915 return None -def get_standard_logging_metadata( - metadata: Optional[Dict[str, Any]] -) -> StandardLoggingMetadata: - """ - Clean and filter the metadata dictionary to include only the specified keys in StandardLoggingMetadata. - - Args: - metadata (Optional[Dict[str, Any]]): The original metadata dictionary. - - Returns: - StandardLoggingMetadata: A StandardLoggingMetadata object containing the cleaned metadata. - - Note: - - If the input metadata is None or not a dictionary, an empty StandardLoggingMetadata object is returned. - - If 'user_api_key' is present in metadata and is a valid SHA256 hash, it's stored as 'user_api_key_hash'. - """ - # Initialize with default values - clean_metadata = StandardLoggingMetadata( - user_api_key_hash=None, - user_api_key_alias=None, - user_api_key_team_id=None, - user_api_key_user_id=None, - user_api_key_team_alias=None, - spend_logs_metadata=None, - requester_ip_address=None, - requester_metadata=None, - ) - if isinstance(metadata, dict): - # Filter the metadata dictionary to include only the specified keys - clean_metadata = StandardLoggingMetadata( - **{ # type: ignore - key: metadata[key] - for key in StandardLoggingMetadata.__annotations__.keys() - if key in metadata - } - ) - - if metadata.get("user_api_key") is not None: - if is_valid_sha256_hash(str(metadata.get("user_api_key"))): - clean_metadata["user_api_key_hash"] = metadata.get( - "user_api_key" - ) # this is the hash - return clean_metadata - - def scrub_sensitive_keys_in_metadata(litellm_params: Optional[dict]): if litellm_params is None: litellm_params = {} diff --git a/tests/logging_callback_tests/test_standard_logging_payload.py b/tests/logging_callback_tests/test_standard_logging_payload.py new file mode 100644 index 0000000000..f6599a0056 --- /dev/null +++ b/tests/logging_callback_tests/test_standard_logging_payload.py @@ -0,0 +1,67 @@ +""" +Unit tests for StandardLoggingPayloadSetup +""" + +import json +import os +import sys +from datetime import datetime +from unittest.mock import AsyncMock + +from pydantic.main import Model + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system-path + +import pytest +import litellm +from litellm.types.utils import Usage +from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup + + +@pytest.mark.parametrize( + "response_obj,expected_values", + [ + # Test None input + (None, (0, 0, 0)), + # Test empty dict + ({}, (0, 0, 0)), + # Test valid usage dict + ( + { + "usage": { + "prompt_tokens": 10, + "completion_tokens": 20, + "total_tokens": 30, + } + }, + (10, 20, 30), + ), + # Test with litellm.Usage object + ( + {"usage": Usage(prompt_tokens=15, completion_tokens=25, total_tokens=40)}, + (15, 25, 40), + ), + # Test invalid usage type + ({"usage": "invalid"}, (0, 0, 0)), + # Test None usage + ({"usage": None}, (0, 0, 0)), + ], +) +def test_get_usage(response_obj, expected_values): + """ + Make sure values returned from get_usage are always integers + """ + + usage = StandardLoggingPayloadSetup.get_usage_from_response_obj(response_obj) + + # Check types + assert isinstance(usage.prompt_tokens, int) + assert isinstance(usage.completion_tokens, int) + assert isinstance(usage.total_tokens, int) + + # Check values + assert usage.prompt_tokens == expected_values[0] + assert usage.completion_tokens == expected_values[1] + assert usage.total_tokens == expected_values[2] From 1cd1d23fdf190a63281d1ca9ceb80e31c2a1d20a Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 19:01:41 -0700 Subject: [PATCH 31/62] LiteLLM Minor Fixes & Improvements (10/23/2024) (#6407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(bedrock.md): clarify bedrock auth in litellm docs * fix(convert_dict_to_response.py): Fixes https://github.com/BerriAI/litellm/issues/6387 * feat(pattern_match_deployments.py): more robust handling for wildcard routes (model_name: custom_route/* -> openai/*) Enables user to expose custom routes to users with dynamic handling * test: add more testing * docs(custom_pricing.md): add debug tutorial for custom pricing * test: skip codestral test - unreachable backend * test: fix test * fix(pattern_matching_deployments.py): fix typing * test: cleanup codestral tests - backend api unavailable * (refactor) prometheus async_log_success_event to be under 100 LOC (#6416) * unit testig for prometheus * unit testing for success metrics * use 1 helper for _increment_token_metrics * use helper for _increment_remaining_budget_metrics * use _increment_remaining_budget_metrics * use _increment_top_level_request_and_spend_metrics * use helper for _set_latency_metrics * remove noqa violation * fix test prometheus * test prometheus * unit testing for all prometheus helper functions * fix prom unit tests * fix unit tests prometheus * fix unit test prom * (refactor) router - use static methods for client init utils (#6420) * use InitalizeOpenAISDKClient * use InitalizeOpenAISDKClient static method * fix # noqa: PLR0915 * (code cleanup) remove unused and undocumented logging integrations - litedebugger, berrispend (#6406) * code cleanup remove unused and undocumented code files * fix unused logging integrations cleanup * bump: version 1.50.3 → 1.50.4 --------- Co-authored-by: Ishaan Jaff --- docs/my-website/docs/providers/bedrock.md | 14 ++-- docs/my-website/docs/proxy/custom_pricing.md | 32 ++++++++- .../convert_dict_to_response.py | 42 +++++++---- litellm/llms/OpenAI/openai.py | 8 +-- .../router_utils/pattern_match_deployments.py | 50 ++++++++++++-- litellm/types/utils.py | 12 +++- .../test_convert_dict_to_chat_completion.py | 38 ++++++++++ .../test_router_pattern_matching.py | 8 +-- .../test_router_helper_utils.py | 69 +++++++++++++++++++ 9 files changed, 235 insertions(+), 38 deletions(-) diff --git a/docs/my-website/docs/providers/bedrock.md b/docs/my-website/docs/providers/bedrock.md index 279098d12c..afd1fee396 100644 --- a/docs/my-website/docs/providers/bedrock.md +++ b/docs/my-website/docs/providers/bedrock.md @@ -9,12 +9,11 @@ LiteLLM requires `boto3` to be installed on your system for Bedrock requests pip install boto3>=1.28.57 ``` -## Required Environment Variables -```python -os.environ["AWS_ACCESS_KEY_ID"] = "" # Access key -os.environ["AWS_SECRET_ACCESS_KEY"] = "" # Secret access key -os.environ["AWS_REGION_NAME"] = "" # us-east-1, us-east-2, us-west-1, us-west-2 -``` +:::info + +LiteLLM uses boto3 to handle authentication. All these options are supported - https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html#credentials. + +::: ## Usage @@ -22,6 +21,7 @@ os.environ["AWS_REGION_NAME"] = "" # us-east-1, us-east-2, us-west-1, us-west-2 Open In Colab + ```python import os from litellm import completion @@ -38,7 +38,7 @@ response = completion( ## LiteLLM Proxy Usage -Here's how to call Anthropic with the LiteLLM Proxy Server +Here's how to call Bedrock with the LiteLLM Proxy Server ### 1. Setup config.yaml diff --git a/docs/my-website/docs/proxy/custom_pricing.md b/docs/my-website/docs/proxy/custom_pricing.md index 8bc42d8ffe..16d634dee4 100644 --- a/docs/my-website/docs/proxy/custom_pricing.md +++ b/docs/my-website/docs/proxy/custom_pricing.md @@ -57,4 +57,34 @@ model_list: api_version: os.envrion/AZURE_API_VERSION input_cost_per_token: 0.000421 # 👈 ONLY to track cost per token output_cost_per_token: 0.000520 # 👈 ONLY to track cost per token -``` \ No newline at end of file +``` + +### Debugging + +If you're custom pricing is not being used or you're seeing errors, please check the following: + +1. Run the proxy with `LITELLM_LOG="DEBUG"` or the `--detailed_debug` cli flag + +```bash +litellm --config /path/to/config.yaml --detailed_debug +``` + +2. Check logs for this line: + +``` +LiteLLM:DEBUG: utils.py:263 - litellm.acompletion +``` + +3. Check if 'input_cost_per_token' and 'output_cost_per_token' are top-level keys in the acompletion function. + +```bash +acompletion( + ..., + input_cost_per_token: my-custom-price, + output_cost_per_token: my-custom-price, +) +``` + +If these keys are not present, LiteLLM will not use your custom pricing. + +If the problem persists, please file an issue on [GitHub](https://github.com/BerriAI/litellm/issues). \ No newline at end of file diff --git a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py index fe69d837c9..95749037da 100644 --- a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py +++ b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py @@ -214,6 +214,28 @@ def _handle_invalid_parallel_tool_calls( return tool_calls +class LiteLLMResponseObjectHandler: + + @staticmethod + def convert_to_image_response( + response_object: dict, + model_response_object: Optional[ImageResponse] = None, + hidden_params: Optional[dict] = None, + ) -> ImageResponse: + + response_object.update({"hidden_params": hidden_params}) + + if model_response_object is None: + model_response_object = ImageResponse(**response_object) + return model_response_object + else: + model_response_dict = model_response_object.model_dump() + + model_response_dict.update(response_object) + model_response_object = ImageResponse(**model_response_dict) + return model_response_object + + def convert_to_model_response_object( # noqa: PLR0915 response_object: Optional[dict] = None, model_response_object: Optional[ @@ -238,7 +260,6 @@ def convert_to_model_response_object( # noqa: PLR0915 ] = None, # used for supporting 'json_schema' on older models ): received_args = locals() - additional_headers = get_response_headers(_response_headers) if hidden_params is None: @@ -427,20 +448,11 @@ def convert_to_model_response_object( # noqa: PLR0915 ): if response_object is None: raise Exception("Error in response object format") - - if model_response_object is None: - model_response_object = ImageResponse() - - if "created" in response_object: - model_response_object.created = response_object["created"] - - if "data" in response_object: - model_response_object.data = response_object["data"] - - if hidden_params is not None: - model_response_object._hidden_params = hidden_params - - return model_response_object + return LiteLLMResponseObjectHandler.convert_to_image_response( + response_object=response_object, + model_response_object=model_response_object, + hidden_params=hidden_params, + ) elif response_type == "audio_transcription" and ( model_response_object is None or isinstance(model_response_object, TranscriptionResponse) diff --git a/litellm/llms/OpenAI/openai.py b/litellm/llms/OpenAI/openai.py index 9cb4beca9d..008296fe79 100644 --- a/litellm/llms/OpenAI/openai.py +++ b/litellm/llms/OpenAI/openai.py @@ -1349,7 +1349,7 @@ class OpenAIChatCompletion(BaseLLM): if aimg_generation is True: return self.aimage_generation(data=data, prompt=prompt, logging_obj=logging_obj, model_response=model_response, api_base=api_base, api_key=api_key, timeout=timeout, client=client, max_retries=max_retries) # type: ignore - openai_client = self._get_openai_client( + openai_client: OpenAI = self._get_openai_client( # type: ignore is_async=False, api_key=api_key, api_base=api_base, @@ -1371,8 +1371,9 @@ class OpenAIChatCompletion(BaseLLM): ) ## COMPLETION CALL - response = openai_client.images.generate(**data, timeout=timeout) # type: ignore - response = response.model_dump() # type: ignore + _response = openai_client.images.generate(**data, timeout=timeout) # type: ignore + + response = _response.model_dump() ## LOGGING logging_obj.post_call( input=prompt, @@ -1380,7 +1381,6 @@ class OpenAIChatCompletion(BaseLLM): additional_args={"complete_input_dict": data}, original_response=response, ) - # return response return convert_to_model_response_object(response_object=response, model_response_object=model_response, response_type="image_generation") # type: ignore except OpenAIError as e: diff --git a/litellm/router_utils/pattern_match_deployments.py b/litellm/router_utils/pattern_match_deployments.py index e92049fac9..a0d631bf71 100644 --- a/litellm/router_utils/pattern_match_deployments.py +++ b/litellm/router_utils/pattern_match_deployments.py @@ -4,6 +4,7 @@ Class to handle llm wildcard routing and regex pattern matching import copy import re +from re import Match from typing import Dict, List, Optional from litellm import get_llm_provider @@ -53,11 +54,12 @@ class PatternMatchRouter: Returns: str: regex pattern """ - # Replace '*' with '.*' for regex matching - regex = pattern.replace("*", ".*") - # Escape other special characters - regex = re.escape(regex).replace(r"\.\*", ".*") - return f"^{regex}$" + # # Replace '*' with '.*' for regex matching + # regex = pattern.replace("*", ".*") + # # Escape other special characters + # regex = re.escape(regex).replace(r"\.\*", ".*") + # return f"^{regex}$" + return re.escape(pattern).replace(r"\*", "(.*)") def route(self, request: Optional[str]) -> Optional[List[Dict]]: """ @@ -84,6 +86,44 @@ class PatternMatchRouter: return None # No matching pattern found + @staticmethod + def set_deployment_model_name( + matched_pattern: Match, + litellm_deployment_litellm_model: str, + ) -> str: + """ + Set the model name for the matched pattern llm deployment + + E.g.: + + model_name: llmengine/* (can be any regex pattern or wildcard pattern) + litellm_params: + model: openai/* + + if model_name = "llmengine/foo" -> model = "openai/foo" + """ + ## BASE CASE: if the deployment model name does not contain a wildcard, return the deployment model name + if "*" not in litellm_deployment_litellm_model: + return litellm_deployment_litellm_model + + wildcard_count = litellm_deployment_litellm_model.count("*") + + # Extract all dynamic segments from the request + dynamic_segments = matched_pattern.groups() + + if len(dynamic_segments) > wildcard_count: + raise ValueError( + f"More wildcards in the deployment model name than the pattern. Wildcard count: {wildcard_count}, dynamic segments count: {len(dynamic_segments)}" + ) + + # Replace the corresponding wildcards in the litellm model pattern with extracted segments + for segment in dynamic_segments: + litellm_deployment_litellm_model = litellm_deployment_litellm_model.replace( + "*", segment, 1 + ) + + return litellm_deployment_litellm_model + def get_pattern( self, model: str, custom_llm_provider: Optional[str] = None ) -> Optional[List[Dict]]: diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 341c9fc8b8..8cc0844b31 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -1177,12 +1177,15 @@ from openai.types.images_response import ImagesResponse as OpenAIImageResponse class ImageResponse(OpenAIImageResponse): _hidden_params: dict = {} + usage: Usage def __init__( self, created: Optional[int] = None, data: Optional[List[ImageObject]] = None, response_ms=None, + usage: Optional[Usage] = None, + hidden_params: Optional[dict] = None, ): if response_ms: _response_ms = response_ms @@ -1204,8 +1207,13 @@ class ImageResponse(OpenAIImageResponse): _data.append(ImageObject(**d)) elif isinstance(d, BaseModel): _data.append(ImageObject(**d.model_dump())) - super().__init__(created=created, data=_data) - self.usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0} + _usage = usage or Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + ) + super().__init__(created=created, data=_data, usage=_usage) # type: ignore + self._hidden_params = hidden_params or {} def __contains__(self, key): # Define custom behavior for the 'in' operator diff --git a/tests/llm_translation/test_llm_response_utils/test_convert_dict_to_chat_completion.py b/tests/llm_translation/test_llm_response_utils/test_convert_dict_to_chat_completion.py index 20d21a39d3..a1d13bcb34 100644 --- a/tests/llm_translation/test_llm_response_utils/test_convert_dict_to_chat_completion.py +++ b/tests/llm_translation/test_llm_response_utils/test_convert_dict_to_chat_completion.py @@ -695,3 +695,41 @@ def test_convert_to_model_response_object_error(): _response_headers=None, convert_tool_call_to_json_mode=False, ) + + +def test_image_generation_openai_with_pydantic_warning(caplog): + try: + import logging + from litellm.types.utils import ImageResponse, ImageObject + + convert_response_args = { + "response_object": { + "created": 1729709945, + "data": [ + { + "b64_json": None, + "revised_prompt": "Generate an image of a baby sea otter. It should look incredibly cute, with big, soulful eyes and a fluffy, wet fur coat. The sea otter should be on its back, as sea otters often do, with its tiny hands holding onto a shell as if it is its precious toy. The background should be a tranquil sea under a clear sky, with soft sunlight reflecting off the waters. The color palette should be soothing with blues, browns, and white.", + "url": "https://oaidalleapiprodscus.blob.core.windows.net/private/org-ikDc4ex8NB5ZzfTf8m5WYVB7/user-JpwZsbIXubBZvan3Y3GchiiB/img-LL0uoOv4CFJIvNYxoNCKB8oc.png?st=2024-10-23T17%3A59%3A05Z&se=2024-10-23T19%3A59%3A05Z&sp=r&sv=2024-08-04&sr=b&rscd=inline&rsct=image/png&skoid=d505667d-d6c1-4a0a-bac7-5c84a87759f8&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-10-22T19%3A26%3A22Z&ske=2024-10-23T19%3A26%3A22Z&sks=b&skv=2024-08-04&sig=Hl4wczJ3H2vZNdLRt/7JvNi6NvQGDnbNkDy15%2Bl3k5s%3D", + } + ], + }, + "model_response_object": ImageResponse( + created=1729709929, + data=[], + ), + "response_type": "image_generation", + "stream": False, + "start_time": None, + "end_time": None, + "hidden_params": None, + "_response_headers": None, + "convert_tool_call_to_json_mode": None, + } + + resp: ImageResponse = convert_to_model_response_object(**convert_response_args) + assert resp is not None + assert resp.data is not None + assert len(resp.data) == 1 + assert isinstance(resp.data[0], ImageObject) + except Exception as e: + pytest.fail(f"Test failed with exception: {e}") diff --git a/tests/local_testing/test_router_pattern_matching.py b/tests/local_testing/test_router_pattern_matching.py index d7e76b88b0..701a62e412 100644 --- a/tests/local_testing/test_router_pattern_matching.py +++ b/tests/local_testing/test_router_pattern_matching.py @@ -42,7 +42,7 @@ def test_add_pattern(): ) router.add_pattern("openai/*", deployment.to_json(exclude_none=True)) assert len(router.patterns) == 1 - assert list(router.patterns.keys())[0] == "^openai/.*$" + assert list(router.patterns.keys())[0] == "openai/(.*)" # try getting the pattern assert router.route(request="openai/gpt-15") == [ @@ -64,7 +64,7 @@ def test_add_pattern_vertex_ai(): ) router.add_pattern("vertex_ai/*", deployment.to_json(exclude_none=True)) assert len(router.patterns) == 1 - assert list(router.patterns.keys())[0] == "^vertex_ai/.*$" + assert list(router.patterns.keys())[0] == "vertex_ai/(.*)" # try getting the pattern assert router.route(request="vertex_ai/gemini-1.5-flash-latest") == [ @@ -99,10 +99,10 @@ def test_pattern_to_regex(): Tests that the pattern is converted to a regex """ router = PatternMatchRouter() - assert router._pattern_to_regex("openai/*") == "^openai/.*$" + assert router._pattern_to_regex("openai/*") == "openai/(.*)" assert ( router._pattern_to_regex("openai/fo::*::static::*") - == "^openai/fo::.*::static::.*$" + == "openai/fo::(.*)::static::(.*)" ) diff --git a/tests/router_unit_tests/test_router_helper_utils.py b/tests/router_unit_tests/test_router_helper_utils.py index a97bf3197e..78e3227648 100644 --- a/tests/router_unit_tests/test_router_helper_utils.py +++ b/tests/router_unit_tests/test_router_helper_utils.py @@ -914,3 +914,72 @@ def test_replace_model_in_jsonl(model_list): router = Router(model_list=model_list) deployments = router.pattern_router.get_deployments_by_pattern(model="claude-3") assert deployments is not None + + +# def test_pattern_match_deployments(model_list): +# from litellm.router_utils.pattern_match_deployments import PatternMatchRouter +# import re + +# patter_router = PatternMatchRouter() + +# request = "fo::hi::static::hello" +# model_name = "fo::*:static::*" + +# model_name_regex = patter_router._pattern_to_regex(model_name) + +# # Match against the request +# match = re.match(model_name_regex, request) + +# print(f"match: {match}") +# print(f"match.end: {match.end()}") +# if match is None: +# raise ValueError("Match not found") +# updated_model = patter_router.set_deployment_model_name( +# matched_pattern=match, litellm_deployment_litellm_model="openai/*" +# ) +# assert updated_model == "openai/fo::hi:static::hello" + + +@pytest.mark.parametrize( + "user_request_model, model_name, litellm_model, expected_model", + [ + ("llmengine/foo", "llmengine/*", "openai/foo", "openai/foo"), + ("llmengine/foo", "llmengine/*", "openai/*", "openai/foo"), + ( + "fo::hi::static::hello", + "fo::*::static::*", + "openai/fo::*:static::*", + "openai/fo::hi:static::hello", + ), + ( + "fo::hi::static::hello", + "fo::*::static::*", + "openai/gpt-3.5-turbo", + "openai/gpt-3.5-turbo", + ), + ], +) +def test_pattern_match_deployment_set_model_name( + user_request_model, model_name, litellm_model, expected_model +): + from re import Match + from litellm.router_utils.pattern_match_deployments import PatternMatchRouter + + pattern_router = PatternMatchRouter() + + import re + + # Convert model_name into a proper regex + model_name_regex = pattern_router._pattern_to_regex(model_name) + + # Match against the request + match = re.match(model_name_regex, user_request_model) + + if match is None: + raise ValueError("Match not found") + + # Call the set_deployment_model_name function + updated_model = pattern_router.set_deployment_model_name(match, litellm_model) + + print(updated_model) # Expected output: "openai/fo::hi:static::hello" + assert updated_model == expected_model From cce118e091b6f206249cb159f572da635eb18926 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 07:19:29 +0400 Subject: [PATCH 32/62] track created, updated at virtual keys --- litellm/proxy/schema.prisma | 2 ++ schema.prisma | 2 ++ 2 files changed, 4 insertions(+) diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index e7e5433514..64045999c9 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -154,6 +154,8 @@ model LiteLLM_VerificationToken { model_spend Json @default("{}") model_max_budget Json @default("{}") budget_id String? + created_at DateTime? @default(now()) @map("created_at") + updated_at DateTime? @default(now()) @updatedAt @map("updated_at") litellm_budget_table LiteLLM_BudgetTable? @relation(fields: [budget_id], references: [budget_id]) } diff --git a/schema.prisma b/schema.prisma index e7e5433514..64045999c9 100644 --- a/schema.prisma +++ b/schema.prisma @@ -154,6 +154,8 @@ model LiteLLM_VerificationToken { model_spend Json @default("{}") model_max_budget Json @default("{}") budget_id String? + created_at DateTime? @default(now()) @map("created_at") + updated_at DateTime? @default(now()) @updatedAt @map("updated_at") litellm_budget_table LiteLLM_BudgetTable? @relation(fields: [budget_id], references: [budget_id]) } From 8cd8d47f47d1b070ca79033cc3c174265517b89e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 07:21:15 +0400 Subject: [PATCH 33/62] add created_at, updated_at for verification token --- litellm/proxy/_types.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 629e002b56..b0463a890c 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -1371,6 +1371,8 @@ class LiteLLM_VerificationToken(LiteLLMBase): blocked: Optional[bool] = None litellm_budget_table: Optional[dict] = None org_id: Optional[str] = None # org id for a given key + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None model_config = ConfigDict(protected_namespaces=()) From cc8dd802093f5c4fed56bea3717855c2e0071fef Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 22:00:24 -0700 Subject: [PATCH 34/62] allow configuring httpx hooks for AsyncHTTPHandler (#6290) (#6415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow configuring httpx hooks for AsyncHTTPHandler (#6290) Co-authored-by: Krish Dholakia * Fixes and minor improvements for Helm Chart (#6402) * reckoner hack * fix default * add extracontainers option * revert chart * fix extracontainers * fix deployment * remove init container * update docs * add helm lint to deploy step * change name * (refactor) prometheus async_log_success_event to be under 100 LOC (#6416) * unit testig for prometheus * unit testing for success metrics * use 1 helper for _increment_token_metrics * use helper for _increment_remaining_budget_metrics * use _increment_remaining_budget_metrics * use _increment_top_level_request_and_spend_metrics * use helper for _set_latency_metrics * remove noqa violation * fix test prometheus * test prometheus * unit testing for all prometheus helper functions * fix prom unit tests * fix unit tests prometheus * fix unit test prom * (refactor) router - use static methods for client init utils (#6420) * use InitalizeOpenAISDKClient * use InitalizeOpenAISDKClient static method * fix # noqa: PLR0915 * (code cleanup) remove unused and undocumented logging integrations - litedebugger, berrispend (#6406) * code cleanup remove unused and undocumented code files * fix unused logging integrations cleanup * update chart version * add circleci tests --------- Co-authored-by: Ishaan Jaff Co-authored-by: Xingyao Wang * fix: fix linting error * fix(http_handler.py): fix linting error --------- Co-authored-by: Alejandro Rodríguez Co-authored-by: Robert Brennan Co-authored-by: Ishaan Jaff Co-authored-by: Xingyao Wang --- .circleci/config.yml | 8 ++- .github/workflows/ghcr_helm_deploy.yml | 5 +- deploy/charts/litellm-helm/Chart.yaml | 2 +- deploy/charts/litellm-helm/README.md | 5 +- .../litellm-helm/templates/deployment.yaml | 70 ++----------------- deploy/charts/litellm-helm/values.yaml | 7 +- litellm/llms/custom_httpx/http_handler.py | 24 +++++-- tests/local_testing/test_utils.py | 25 +++++++ 8 files changed, 60 insertions(+), 86 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c84f5d9417..6bbc95b5a4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -416,15 +416,17 @@ jobs: command: | python -m pip install --upgrade pip pip install ruff - pip install pylint + pip install pylint pip install pyright pip install . + curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash - run: python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) - run: ruff check ./litellm - run: python ./tests/documentation_tests/test_general_setting_keys.py - run: python ./tests/code_coverage_tests/router_code_coverage.py - run: python ./tests/documentation_tests/test_env_keys.py - + - run: helm lint ./deploy/charts/litellm-helm + db_migration_disable_update_check: machine: image: ubuntu-2204:2023.10.1 @@ -1099,4 +1101,4 @@ workflows: branches: only: - main - \ No newline at end of file + diff --git a/.github/workflows/ghcr_helm_deploy.yml b/.github/workflows/ghcr_helm_deploy.yml index 35ea96bfb8..f78dc6f0f3 100644 --- a/.github/workflows/ghcr_helm_deploy.yml +++ b/.github/workflows/ghcr_helm_deploy.yml @@ -50,6 +50,9 @@ jobs: current-version: ${{ steps.current_version.outputs.current-version || '0.1.0' }} version-fragment: 'bug' + - name: Lint helm chart + run: helm lint deploy/charts/litellm-helm + - uses: ./.github/actions/helm-oci-chart-releaser with: name: litellm-helm @@ -61,4 +64,4 @@ jobs: registry_username: ${{ github.actor }} registry_password: ${{ secrets.GITHUB_TOKEN }} update_dependencies: true - \ No newline at end of file + diff --git a/deploy/charts/litellm-helm/Chart.yaml b/deploy/charts/litellm-helm/Chart.yaml index 594bbd76d4..6232a2320d 100644 --- a/deploy/charts/litellm-helm/Chart.yaml +++ b/deploy/charts/litellm-helm/Chart.yaml @@ -24,7 +24,7 @@ version: 0.3.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: v1.46.6 +appVersion: v1.50.2 dependencies: - name: "postgresql" diff --git a/deploy/charts/litellm-helm/README.md b/deploy/charts/litellm-helm/README.md index 02fccdf036..8b2196f577 100644 --- a/deploy/charts/litellm-helm/README.md +++ b/deploy/charts/litellm-helm/README.md @@ -28,14 +28,13 @@ If `db.useStackgresOperator` is used (not yet implemented): | `image.repository` | LiteLLM Proxy image repository | `ghcr.io/berriai/litellm` | | `image.pullPolicy` | LiteLLM Proxy image pull policy | `IfNotPresent` | | `image.tag` | Overrides the image tag whose default the latest version of LiteLLM at the time this chart was published. | `""` | -| `image.dbReadyImage` | On Pod startup, an initContainer is used to make sure the Postgres database is available before attempting to start LiteLLM. This field specifies the image to use as that initContainer. | `docker.io/bitnami/postgresql` | -| `image.dbReadyTag` | Tag for the above image. If not specified, "latest" is used. | `""` | | `imagePullSecrets` | Registry credentials for the LiteLLM and initContainer images. | `[]` | | `serviceAccount.create` | Whether or not to create a Kubernetes Service Account for this deployment. The default is `false` because LiteLLM has no need to access the Kubernetes API. | `false` | | `service.type` | Kubernetes Service type (e.g. `LoadBalancer`, `ClusterIP`, etc.) | `ClusterIP` | | `service.port` | TCP port that the Kubernetes Service will listen on. Also the TCP port within the Pod that the proxy will listen on. | `4000` | | `ingress.*` | See [values.yaml](./values.yaml) for example settings | N/A | | `proxy_config.*` | See [values.yaml](./values.yaml) for default settings. See [example_config_yaml](../../../litellm/proxy/example_config_yaml/) for configuration examples. | N/A | +| `extraContainers[]` | An array of additional containers to be deployed as sidecars alongside the LiteLLM Proxy. | `[]` | #### Example `environmentSecrets` Secret @@ -127,4 +126,4 @@ kubectl -n litellm get secret -litellm-masterkey -o jsonpath="{.data.ma At the time of writing, the Admin UI is unable to add models. This is because it would need to update the `config.yaml` file which is a exposed ConfigMap, and therefore, read-only. This is a limitation of this helm chart, not the Admin UI -itself. \ No newline at end of file +itself. diff --git a/deploy/charts/litellm-helm/templates/deployment.yaml b/deploy/charts/litellm-helm/templates/deployment.yaml index 7e5faac3c3..7f4e876532 100644 --- a/deploy/charts/litellm-helm/templates/deployment.yaml +++ b/deploy/charts/litellm-helm/templates/deployment.yaml @@ -31,71 +31,6 @@ spec: serviceAccountName: {{ include "litellm.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} - initContainers: - - name: db-ready - securityContext: - {{- toYaml .Values.securityContext | nindent 12 }} - image: "{{ .Values.image.dbReadyImage }}:{{ .Values.image.dbReadyTag | default("16.1.0-debian-11-r20") }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - env: - {{- if .Values.db.deployStandalone }} - - name: DATABASE_USERNAME - valueFrom: - secretKeyRef: - name: {{ include "litellm.fullname" . }}-dbcredentials - key: username - - name: PGPASSWORD - valueFrom: - secretKeyRef: - name: {{ include "litellm.fullname" . }}-dbcredentials - key: password - - name: DATABASE_HOST - value: {{ .Release.Name }}-postgresql - - name: DATABASE_NAME - value: litellm - {{- else if .Values.db.useExisting }} - - name: DATABASE_USERNAME - valueFrom: - secretKeyRef: - name: {{ .Values.db.secret.name }} - key: {{ .Values.db.secret.usernameKey }} - - name: PGPASSWORD - valueFrom: - secretKeyRef: - name: {{ .Values.db.secret.name }} - key: {{ .Values.db.secret.passwordKey }} - - name: DATABASE_HOST - value: {{ .Values.db.endpoint }} - - name: DATABASE_NAME - value: {{ .Values.db.database }} - {{- end }} - command: - - sh - - -c - - | - # Maximum wait time will be (limit * 2) seconds. - limit=60 - current=0 - ret=1 - while [ $current -lt $limit ] && [ $ret -ne 0 ]; do - echo "Waiting for database to be ready $current" - psql -U $(DATABASE_USERNAME) -h $(DATABASE_HOST) -l - ret=$? - current=$(( $current + 1 )) - sleep 2 - done - if [ $ret -eq 0 ]; then - echo "Database is ready" - else - echo "Database failed to become ready before we gave up waiting." - fi - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{ if .Values.securityContext.readOnlyRootFilesystem }} - volumeMounts: - - name: tmp - mountPath: /tmp - {{ end }} containers: - name: {{ include "litellm.name" . }} securityContext: @@ -203,6 +138,9 @@ spec: {{- with .Values.volumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} + {{- with .Values.extraContainers }} + {{- toYaml . | nindent 8 }} + {{- end }} volumes: {{ if .Values.securityContext.readOnlyRootFilesystem }} - name: tmp @@ -235,4 +173,4 @@ spec: {{- with .Values.tolerations }} tolerations: {{- toYaml . | nindent 8 }} - {{- end }} \ No newline at end of file + {{- end }} diff --git a/deploy/charts/litellm-helm/values.yaml b/deploy/charts/litellm-helm/values.yaml index 0e11c3f617..a2c55f2faa 100644 --- a/deploy/charts/litellm-helm/values.yaml +++ b/deploy/charts/litellm-helm/values.yaml @@ -7,16 +7,11 @@ replicaCount: 1 image: # Use "ghcr.io/berriai/litellm-database" for optimized image with database repository: ghcr.io/berriai/litellm-database - pullPolicy: IfNotPresent + pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. # tag: "main-latest" tag: "" - # Image and tag used for the init container to check and wait for the - # readiness of the postgres database. - dbReadyImage: docker.io/bitnami/postgresql - dbReadyTag: "" - imagePullSecrets: [] nameOverride: "litellm" fullnameOverride: "" diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index a2b592ef8e..89b294584e 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -1,7 +1,7 @@ import asyncio import os import traceback -from typing import TYPE_CHECKING, Any, Mapping, Optional, Union +from typing import TYPE_CHECKING, Any, Callable, List, Mapping, Optional, Union import httpx from httpx import USE_CLIENT_DEFAULT @@ -32,15 +32,20 @@ class AsyncHTTPHandler: def __init__( self, timeout: Optional[Union[float, httpx.Timeout]] = None, + event_hooks: Optional[Mapping[str, List[Callable[..., Any]]]] = None, concurrent_limit=1000, ): self.timeout = timeout + self.event_hooks = event_hooks self.client = self.create_client( - timeout=timeout, concurrent_limit=concurrent_limit + timeout=timeout, concurrent_limit=concurrent_limit, event_hooks=event_hooks ) def create_client( - self, timeout: Optional[Union[float, httpx.Timeout]], concurrent_limit: int + self, + timeout: Optional[Union[float, httpx.Timeout]], + concurrent_limit: int, + event_hooks: Optional[Mapping[str, List[Callable[..., Any]]]], ) -> httpx.AsyncClient: # SSL certificates (a.k.a CA bundle) used to verify the identity of requested hosts. @@ -55,6 +60,7 @@ class AsyncHTTPHandler: # Create a client with a connection pool return httpx.AsyncClient( + event_hooks=event_hooks, timeout=timeout, limits=httpx.Limits( max_connections=concurrent_limit, @@ -114,7 +120,9 @@ class AsyncHTTPHandler: return response except (httpx.RemoteProtocolError, httpx.ConnectError): # Retry the request with a new session if there is a connection error - new_client = self.create_client(timeout=timeout, concurrent_limit=1) + new_client = self.create_client( + timeout=timeout, concurrent_limit=1, event_hooks=self.event_hooks + ) try: return await self.single_connection_post_request( url=url, @@ -172,7 +180,9 @@ class AsyncHTTPHandler: return response except (httpx.RemoteProtocolError, httpx.ConnectError): # Retry the request with a new session if there is a connection error - new_client = self.create_client(timeout=timeout, concurrent_limit=1) + new_client = self.create_client( + timeout=timeout, concurrent_limit=1, event_hooks=self.event_hooks + ) try: return await self.single_connection_post_request( url=url, @@ -229,7 +239,9 @@ class AsyncHTTPHandler: return response except (httpx.RemoteProtocolError, httpx.ConnectError): # Retry the request with a new session if there is a connection error - new_client = self.create_client(timeout=timeout, concurrent_limit=1) + new_client = self.create_client( + timeout=timeout, concurrent_limit=1, event_hooks=self.event_hooks + ) try: return await self.single_connection_post_request( url=url, diff --git a/tests/local_testing/test_utils.py b/tests/local_testing/test_utils.py index 684e41da8c..9c26da614a 100644 --- a/tests/local_testing/test_utils.py +++ b/tests/local_testing/test_utils.py @@ -15,6 +15,7 @@ sys.path.insert( import pytest import litellm +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, headers from litellm.proxy.utils import ( _duration_in_seconds, _extract_from_regex, @@ -830,6 +831,29 @@ def test_is_base64_encoded(): assert is_base64_encoded(s=base64_image) is True +@mock.patch("httpx.AsyncClient") +@mock.patch.dict(os.environ, {"SSL_VERIFY": "/certificate.pem", "SSL_CERTIFICATE": "/client.pem"}, clear=True) +def test_async_http_handler(mock_async_client): + import httpx + + timeout = 120 + event_hooks = {"request": [lambda r: r]} + concurrent_limit = 2 + + AsyncHTTPHandler(timeout, event_hooks, concurrent_limit) + + mock_async_client.assert_called_with( + cert="/client.pem", + event_hooks=event_hooks, + headers=headers, + limits=httpx.Limits( + max_connections=concurrent_limit, + max_keepalive_connections=concurrent_limit, + ), + timeout=timeout, + verify="/certificate.pem", + ) + @pytest.mark.parametrize( "model, expected_bool", [("gpt-3.5-turbo", False), ("gpt-4o-audio-preview", True)] ) @@ -842,3 +866,4 @@ def test_supports_audio_input(model, expected_bool): supports_pc = supports_audio_input(model=model) assert supports_pc == expected_bool + From 4e310051c7404e5a2f5c41e0643cb9d03ffcc016 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 22:02:15 -0700 Subject: [PATCH 35/62] =?UTF-8?q?feat(proxy=5Fserver.py):=20check=20if=20v?= =?UTF-8?q?iews=20exist=20on=20proxy=20server=20startup=20+=E2=80=A6=20(#6?= =?UTF-8?q?360)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(proxy_server.py): check if views exist on proxy server startup + refactor startup event logic to <50 LOC * refactor(redis_cache.py): use a default cache value when writing to r… (#6358) * refactor(redis_cache.py): use a default cache value when writing to redis prevent redis from blowing up in high traffic * refactor(redis_cache.py): refactor all cache writes to use self.get_ttl ensures default ttl always used when writing to redis Prevents redis db from blowing up in prod * feat(proxy_cli.py): add new 'log_config' cli param (#6352) * feat(proxy_cli.py): add new 'log_config' cli param Allows passing logging.conf to uvicorn on startup * docs(cli.md): add logging conf to uvicorn cli docs * fix(get_llm_provider_logic.py): fix default api base for litellm_proxy Fixes https://github.com/BerriAI/litellm/issues/6332 * feat(openai_like/embedding): Add support for jina ai embeddings Closes https://github.com/BerriAI/litellm/issues/6337 * docs(deploy.md): update entrypoint.sh filepath post-refactor Fixes outdated docs * feat(prometheus.py): emit time_to_first_token metric on prometheus Closes https://github.com/BerriAI/litellm/issues/6334 * fix(prometheus.py): only emit time to first token metric if stream is True enables more accurate ttft usage * test: handle vertex api instability * fix(get_llm_provider_logic.py): fix import * fix(openai.py): fix deepinfra default api base * fix(anthropic/transformation.py): remove anthropic beta header (#6361) * docs(sidebars.js): add jina ai embedding to docs * docs(sidebars.js): add jina ai to left nav * bump: version 1.50.1 → 1.50.2 * langfuse use helper for get_langfuse_logging_config * Refactor: apply early return (#6369) * (refactor) remove berrispendLogger - unused logging integration (#6363) * fix remove berrispendLogger * remove unused clickhouse logger * fix docs configs.md * (fix) standard logging metadata + add unit testing (#6366) * fix setting StandardLoggingMetadata * add unit testing for standard logging metadata * fix otel logging test * fix linting * fix typing * Revert "(fix) standard logging metadata + add unit testing (#6366)" (#6381) This reverts commit 8359cb6fa9bf7b0bf4f3df630cf8666adffa2813. * add new 35 mode lcard (#6378) * Add claude 3 5 sonnet 20241022 models for all provides (#6380) * Add Claude 3.5 v2 on Amazon Bedrock and Vertex AI. * added anthropic/claude-3-5-sonnet-20241022 * add new 35 mode lcard --------- Co-authored-by: Paul Gauthier Co-authored-by: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> * test(skip-flaky-google-context-caching-test): google is not reliable. their sample code is also not working * test(test_alangfuse.py): handle flaky langfuse test better * (feat) Arize - Allow using Arize HTTP endpoint (#6364) * arize use helper for get_arize_opentelemetry_config * use helper to get Arize OTEL config * arize add helpers for arize * docs allow using arize http endpoint * fix importing OTEL for Arize * use static methods for ArizeLogger * fix ArizeLogger tests * Litellm dev 10 22 2024 (#6384) * fix(utils.py): add 'disallowed_special' for token counting on .encode() Fixes error when '< endoftext >' in string * Revert "(fix) standard logging metadata + add unit testing (#6366)" (#6381) This reverts commit 8359cb6fa9bf7b0bf4f3df630cf8666adffa2813. * add new 35 mode lcard (#6378) * Add claude 3 5 sonnet 20241022 models for all provides (#6380) * Add Claude 3.5 v2 on Amazon Bedrock and Vertex AI. * added anthropic/claude-3-5-sonnet-20241022 * add new 35 mode lcard --------- Co-authored-by: Paul Gauthier Co-authored-by: lowjiansheng <15527690+lowjiansheng@users.noreply.github.com> * test(skip-flaky-google-context-caching-test): google is not reliable. their sample code is also not working * Fix metadata being overwritten in speech() (#6295) * fix: adding missing redis cluster kwargs (#6318) Co-authored-by: Ali Arian * Add support for `max_completion_tokens` in Azure OpenAI (#6376) Now that Azure supports `max_completion_tokens`, no need for special handling for this param and let it pass thru. More details: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models?tabs=python-secure#api-support * build(model_prices_and_context_window.json): add voyage-finance-2 pricing Closes https://github.com/BerriAI/litellm/issues/6371 * build(model_prices_and_context_window.json): fix llama3.1 pricing model name on map Closes https://github.com/BerriAI/litellm/issues/6310 * feat(realtime_streaming.py): just log specific events Closes https://github.com/BerriAI/litellm/issues/6267 * fix(utils.py): more robust checking if unmapped vertex anthropic model belongs to that family of models Fixes https://github.com/BerriAI/litellm/issues/6383 * Fix Ollama stream handling for tool calls with None content (#6155) * test(test_max_completions): update test now that azure supports 'max_completion_tokens' * fix(handler.py): fix linting error --------- Co-authored-by: Ishaan Jaff Co-authored-by: Low Jian Sheng <15527690+lowjiansheng@users.noreply.github.com> Co-authored-by: David Manouchehri Co-authored-by: Paul Gauthier Co-authored-by: John HU Co-authored-by: Ali Arian <113945203+ali-arian@users.noreply.github.com> Co-authored-by: Ali Arian Co-authored-by: Anand Taralika <46954145+taralika@users.noreply.github.com> Co-authored-by: Nolan Tremelling <34580718+NolanTrem@users.noreply.github.com> * bump: version 1.50.2 → 1.50.3 * build(deps): bump http-proxy-middleware in /docs/my-website (#6395) Bumps [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v2.0.7/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v2.0.6...v2.0.7) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * (docs + testing) Correctly document the timeout value used by litellm proxy is 6000 seconds + add to best practices for prod (#6339) * fix docs use documented timeout * document request timeout * add test for litellm.request_timeout * add test for checking value of timeout * (refactor) move convert dict to model response to llm_response_utils/ (#6393) * refactor move convert dict to model response * fix imports * fix import _handle_invalid_parallel_tool_calls * (refactor) litellm.Router client initialization utils (#6394) * refactor InitalizeOpenAISDKClient * use helper func for _should_create_openai_sdk_client_for_model * use static methods for set client on litellm router * reduce LOC in _get_client_initialization_params * fix _should_create_openai_sdk_client_for_model * code quality fix * test test_should_create_openai_sdk_client_for_model * test test_get_client_initialization_params_openai * fix mypy linting errors * fix OpenAISDKClientInitializationParams * test_get_client_initialization_params_all_env_vars * test_get_client_initialization_params_azure_ai_studio_mistral * test_get_client_initialization_params_default_values * fix _get_client_initialization_params * (fix) Langfuse key based logging (#6372) * langfuse use helper for get_langfuse_logging_config * fix get_langfuse_logger_for_request * fix import * fix get_langfuse_logger_for_request * test_get_langfuse_logger_for_request_with_dynamic_params * unit testing for test_get_langfuse_logger_for_request_with_no_dynamic_params * parameterized langfuse testing * fix langfuse test * fix langfuse logging * fix test_aaalangfuse_logging_metadata * fix langfuse log metadata test * fix langfuse logger * use create_langfuse_logger_from_credentials * fix test_get_langfuse_logger_for_request_with_no_dynamic_params * fix correct langfuse/ folder structure * use static methods for langfuse logger * add commment on langfuse handler * fix linting error * add unit testing for langfuse logging * fix linting * fix failure handler langfuse * Revert "(refactor) litellm.Router client initialization utils (#6394)" (#6403) This reverts commit b70147f63b5ad95d90be371a50c6248fe21b20e8. * def test_text_completion_with_echo(stream): (#6401) test * fix linting - remove # noqa PLR0915 from fixed function * test: cleanup codestral tests - backend api unavailable * (refactor) prometheus async_log_success_event to be under 100 LOC (#6416) * unit testig for prometheus * unit testing for success metrics * use 1 helper for _increment_token_metrics * use helper for _increment_remaining_budget_metrics * use _increment_remaining_budget_metrics * use _increment_top_level_request_and_spend_metrics * use helper for _set_latency_metrics * remove noqa violation * fix test prometheus * test prometheus * unit testing for all prometheus helper functions * fix prom unit tests * fix unit tests prometheus * fix unit test prom * (refactor) router - use static methods for client init utils (#6420) * use InitalizeOpenAISDKClient * use InitalizeOpenAISDKClient static method * fix # noqa: PLR0915 * (code cleanup) remove unused and undocumented logging integrations - litedebugger, berrispend (#6406) * code cleanup remove unused and undocumented code files * fix unused logging integrations cleanup * bump: version 1.50.3 → 1.50.4 --------- Signed-off-by: dependabot[bot] Co-authored-by: Ishaan Jaff Co-authored-by: Hakan Taşköprü Co-authored-by: Low Jian Sheng <15527690+lowjiansheng@users.noreply.github.com> Co-authored-by: David Manouchehri Co-authored-by: Paul Gauthier Co-authored-by: John HU Co-authored-by: Ali Arian <113945203+ali-arian@users.noreply.github.com> Co-authored-by: Ali Arian Co-authored-by: Anand Taralika <46954145+taralika@users.noreply.github.com> Co-authored-by: Nolan Tremelling <34580718+NolanTrem@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- litellm/integrations/prometheus.py | 2 + .../convert_dict_to_response.py | 3 + litellm/proxy/_new_secret_config.yaml | 1 - litellm/proxy/proxy_server.py | 358 +++++++++++------- litellm/proxy/utils.py | 25 ++ tests/local_testing/test_proxy_server.py | 24 ++ 6 files changed, 271 insertions(+), 142 deletions(-) diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index bf19c364ef..54563ab61e 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -346,12 +346,14 @@ class PrometheusLogger(CustomLogger): standard_logging_payload: Optional[StandardLoggingPayload] = kwargs.get( "standard_logging_object" ) + if standard_logging_payload is None or not isinstance( standard_logging_payload, dict ): raise ValueError( f"standard_logging_object is required, got={standard_logging_payload}" ) + model = kwargs.get("model", "") litellm_params = kwargs.get("litellm_params", {}) or {} _metadata = litellm_params.get("metadata", {}) diff --git a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py index 95749037da..76077ad463 100644 --- a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py +++ b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py @@ -260,6 +260,7 @@ def convert_to_model_response_object( # noqa: PLR0915 ] = None, # used for supporting 'json_schema' on older models ): received_args = locals() + additional_headers = get_response_headers(_response_headers) if hidden_params is None: @@ -448,11 +449,13 @@ def convert_to_model_response_object( # noqa: PLR0915 ): if response_object is None: raise Exception("Error in response object format") + return LiteLLMResponseObjectHandler.convert_to_image_response( response_object=response_object, model_response_object=model_response_object, hidden_params=hidden_params, ) + elif response_type == "audio_transcription" and ( model_response_object is None or isinstance(model_response_object, TranscriptionResponse) diff --git a/litellm/proxy/_new_secret_config.yaml b/litellm/proxy/_new_secret_config.yaml index 2cdf35b705..ee56b33668 100644 --- a/litellm/proxy/_new_secret_config.yaml +++ b/litellm/proxy/_new_secret_config.yaml @@ -48,4 +48,3 @@ router_settings: redis_host: os.environ/REDIS_HOST redis_port: os.environ/REDIS_PORT redis_password: os.environ/REDIS_PASSWORD - diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 163d2ff25d..bcdd4e86c2 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -638,18 +638,6 @@ def _resolve_pydantic_type(typ) -> List: return typs -def prisma_setup(database_url: Optional[str]): - global prisma_client, proxy_logging_obj, user_api_key_cache - - if database_url is not None: - try: - prisma_client = PrismaClient( - database_url=database_url, proxy_logging_obj=proxy_logging_obj - ) - except Exception as e: - raise e - - def load_from_azure_key_vault(use_azure_key_vault: bool = False): if use_azure_key_vault is False: return @@ -1543,7 +1531,6 @@ class ProxyConfig: ## INIT PROXY REDIS USAGE CLIENT ## redis_usage_cache = litellm.cache.cache - async def get_config(self, config_file_path: Optional[str] = None) -> dict: """ Load config file @@ -2796,137 +2783,55 @@ def giveup(e): return result -@router.on_event("startup") -async def startup_event(): # noqa: PLR0915 - global prisma_client, master_key, use_background_health_checks, llm_router, llm_model_list, general_settings, proxy_budget_rescheduler_min_time, proxy_budget_rescheduler_max_time, litellm_proxy_admin_name, db_writer_client, store_model_in_db, premium_user, _license_check - import json +class ProxyStartupEvent: + @classmethod + def _initialize_startup_logging( + cls, + llm_router: Optional[litellm.Router], + proxy_logging_obj: ProxyLogging, + redis_usage_cache: Optional[RedisCache], + ): + """Initialize logging and alerting on startup""" + ## COST TRACKING ## + cost_tracking() - init_verbose_loggers() + ## Error Tracking ## + error_tracking() - ### LOAD MASTER KEY ### - # check if master key set in environment - load from there - master_key = get_secret("LITELLM_MASTER_KEY", None) # type: ignore - # check if DATABASE_URL in environment - load from there - if prisma_client is None: - _db_url: Optional[str] = get_secret("DATABASE_URL", None) # type: ignore - prisma_setup(database_url=_db_url) + proxy_logging_obj.startup_event( + llm_router=llm_router, redis_usage_cache=redis_usage_cache + ) - ### LOAD CONFIG ### - worker_config: Optional[Union[str, dict]] = get_secret("WORKER_CONFIG") # type: ignore - env_config_yaml: Optional[str] = get_secret_str("CONFIG_FILE_PATH") - verbose_proxy_logger.debug("worker_config: %s", worker_config) - # check if it's a valid file path - if env_config_yaml is not None: - if os.path.isfile(env_config_yaml) and proxy_config.is_yaml( - config_file_path=env_config_yaml - ): - ( - llm_router, - llm_model_list, - general_settings, - ) = await proxy_config.load_config( - router=llm_router, config_file_path=env_config_yaml - ) - elif worker_config is not None: - if ( - isinstance(worker_config, str) - and os.path.isfile(worker_config) - and proxy_config.is_yaml(config_file_path=worker_config) - ): - ( - llm_router, - llm_model_list, - general_settings, - ) = await proxy_config.load_config( - router=llm_router, config_file_path=worker_config - ) - elif os.environ.get("LITELLM_CONFIG_BUCKET_NAME") is not None and isinstance( - worker_config, str - ): - ( - llm_router, - llm_model_list, - general_settings, - ) = await proxy_config.load_config( - router=llm_router, config_file_path=worker_config - ) - elif isinstance(worker_config, dict): - await initialize(**worker_config) + @classmethod + def _initialize_jwt_auth( + cls, + general_settings: dict, + prisma_client: Optional[PrismaClient], + user_api_key_cache: DualCache, + ): + """Initialize JWT auth on startup""" + if general_settings.get("litellm_jwtauth", None) is not None: + for k, v in general_settings["litellm_jwtauth"].items(): + if isinstance(v, str) and v.startswith("os.environ/"): + general_settings["litellm_jwtauth"][k] = get_secret(v) + litellm_jwtauth = LiteLLM_JWTAuth(**general_settings["litellm_jwtauth"]) else: - # if not, assume it's a json string - worker_config = json.loads(worker_config) - if isinstance(worker_config, dict): - await initialize(**worker_config) - - ## CHECK PREMIUM USER - verbose_proxy_logger.debug( - "litellm.proxy.proxy_server.py::startup() - CHECKING PREMIUM USER - {}".format( - premium_user + litellm_jwtauth = LiteLLM_JWTAuth() + jwt_handler.update_environment( + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + litellm_jwtauth=litellm_jwtauth, ) - ) - if premium_user is False: - premium_user = _license_check.is_premium() - verbose_proxy_logger.debug( - "litellm.proxy.proxy_server.py::startup() - PREMIUM USER value - {}".format( - premium_user - ) - ) - - ## COST TRACKING ## - cost_tracking() - - ## Error Tracking ## - error_tracking() - - ## UPDATE SLACK ALERTING ## - proxy_logging_obj.slack_alerting_instance.update_values(llm_router=llm_router) - - db_writer_client = HTTPHandler() - - ## UPDATE INTERNAL USAGE CACHE ## - proxy_logging_obj.update_values( - redis_cache=redis_usage_cache - ) # used by parallel request limiter for rate limiting keys across instances - - proxy_logging_obj._init_litellm_callbacks( - llm_router=llm_router - ) # INITIALIZE LITELLM CALLBACKS ON SERVER STARTUP <- do this to catch any logging errors on startup, not when calls are being made - - if "daily_reports" in proxy_logging_obj.slack_alerting_instance.alert_types: - asyncio.create_task( - proxy_logging_obj.slack_alerting_instance._run_scheduled_daily_report( - llm_router=llm_router - ) - ) # RUN DAILY REPORT (if scheduled) - - ## JWT AUTH ## - if general_settings.get("litellm_jwtauth", None) is not None: - for k, v in general_settings["litellm_jwtauth"].items(): - if isinstance(v, str) and v.startswith("os.environ/"): - general_settings["litellm_jwtauth"][k] = get_secret(v) - litellm_jwtauth = LiteLLM_JWTAuth(**general_settings["litellm_jwtauth"]) - else: - litellm_jwtauth = LiteLLM_JWTAuth() - jwt_handler.update_environment( - prisma_client=prisma_client, - user_api_key_cache=user_api_key_cache, - litellm_jwtauth=litellm_jwtauth, - ) - - if use_background_health_checks: - asyncio.create_task( - _run_background_health_check() - ) # start the background health check coroutine. - - if prompt_injection_detection_obj is not None: - prompt_injection_detection_obj.update_environment(router=llm_router) - - verbose_proxy_logger.debug("prisma_client: %s", prisma_client) - if prisma_client is not None: - await prisma_client.connect() - - if prisma_client is not None and master_key is not None: + @classmethod + def _add_master_key_hash_to_db( + cls, + master_key: str, + prisma_client: PrismaClient, + litellm_proxy_admin_name: str, + general_settings: dict, + ): + """Adds master key hash to db for cost tracking""" if os.getenv("PROXY_ADMIN_ID", None) is not None: litellm_proxy_admin_name = os.getenv( "PROXY_ADMIN_ID", litellm_proxy_admin_name @@ -2951,7 +2856,9 @@ async def startup_event(): # noqa: PLR0915 ) asyncio.create_task(task_1) - if prisma_client is not None and litellm.max_budget > 0: + @classmethod + def _add_proxy_budget_to_db(cls, litellm_proxy_budget_name: str): + """Adds a global proxy budget to db""" if litellm.budget_duration is None: raise Exception( "budget_duration not set on Proxy. budget_duration is required to use max_budget." @@ -2977,8 +2884,18 @@ async def startup_event(): # noqa: PLR0915 ) ) - ### START BATCH WRITING DB + CHECKING NEW MODELS### - if prisma_client is not None: + @classmethod + async def initialize_scheduled_background_jobs( + cls, + general_settings: dict, + prisma_client: PrismaClient, + proxy_budget_rescheduler_min_time: int, + proxy_budget_rescheduler_max_time: int, + proxy_batch_write_at: int, + proxy_logging_obj: ProxyLogging, + store_model_in_db: bool, + ): + """Initializes scheduled background jobs""" scheduler = AsyncIOScheduler() interval = random.randint( proxy_budget_rescheduler_min_time, proxy_budget_rescheduler_max_time @@ -3067,6 +2984,165 @@ async def startup_event(): # noqa: PLR0915 scheduler.start() + @classmethod + def _setup_prisma_client( + cls, + database_url: Optional[str], + proxy_logging_obj: ProxyLogging, + user_api_key_cache: DualCache, + ) -> Optional[PrismaClient]: + """ + - Sets up prisma client + - Adds necessary views to proxy + """ + prisma_client: Optional[PrismaClient] = None + if database_url is not None: + try: + prisma_client = PrismaClient( + database_url=database_url, proxy_logging_obj=proxy_logging_obj + ) + except Exception as e: + raise e + + ## Add necessary views to proxy ## + asyncio.create_task( + prisma_client.check_view_exists() + ) # check if all necessary views exist. Don't block execution + + return prisma_client + + +@router.on_event("startup") +async def startup_event(): + global prisma_client, master_key, use_background_health_checks, llm_router, llm_model_list, general_settings, proxy_budget_rescheduler_min_time, proxy_budget_rescheduler_max_time, litellm_proxy_admin_name, db_writer_client, store_model_in_db, premium_user, _license_check + import json + + init_verbose_loggers() + + ### LOAD MASTER KEY ### + # check if master key set in environment - load from there + master_key = get_secret("LITELLM_MASTER_KEY", None) # type: ignore + # check if DATABASE_URL in environment - load from there + if prisma_client is None: + _db_url: Optional[str] = get_secret("DATABASE_URL", None) # type: ignore + prisma_client = ProxyStartupEvent._setup_prisma_client( + database_url=_db_url, + proxy_logging_obj=proxy_logging_obj, + user_api_key_cache=user_api_key_cache, + ) + + ### LOAD CONFIG ### + worker_config: Optional[Union[str, dict]] = get_secret("WORKER_CONFIG") # type: ignore + env_config_yaml: Optional[str] = get_secret_str("CONFIG_FILE_PATH") + verbose_proxy_logger.debug("worker_config: %s", worker_config) + # check if it's a valid file path + if env_config_yaml is not None: + if os.path.isfile(env_config_yaml) and proxy_config.is_yaml( + config_file_path=env_config_yaml + ): + ( + llm_router, + llm_model_list, + general_settings, + ) = await proxy_config.load_config( + router=llm_router, config_file_path=env_config_yaml + ) + elif worker_config is not None: + if ( + isinstance(worker_config, str) + and os.path.isfile(worker_config) + and proxy_config.is_yaml(config_file_path=worker_config) + ): + ( + llm_router, + llm_model_list, + general_settings, + ) = await proxy_config.load_config( + router=llm_router, config_file_path=worker_config + ) + elif os.environ.get("LITELLM_CONFIG_BUCKET_NAME") is not None and isinstance( + worker_config, str + ): + ( + llm_router, + llm_model_list, + general_settings, + ) = await proxy_config.load_config( + router=llm_router, config_file_path=worker_config + ) + elif isinstance(worker_config, dict): + await initialize(**worker_config) + else: + # if not, assume it's a json string + worker_config = json.loads(worker_config) + if isinstance(worker_config, dict): + await initialize(**worker_config) + + ## CHECK PREMIUM USER + verbose_proxy_logger.debug( + "litellm.proxy.proxy_server.py::startup() - CHECKING PREMIUM USER - {}".format( + premium_user + ) + ) + if premium_user is False: + premium_user = _license_check.is_premium() + + verbose_proxy_logger.debug( + "litellm.proxy.proxy_server.py::startup() - PREMIUM USER value - {}".format( + premium_user + ) + ) + + ProxyStartupEvent._initialize_startup_logging( + llm_router=llm_router, + proxy_logging_obj=proxy_logging_obj, + redis_usage_cache=redis_usage_cache, + ) + + ## JWT AUTH ## + ProxyStartupEvent._initialize_jwt_auth( + general_settings=general_settings, + prisma_client=prisma_client, + user_api_key_cache=user_api_key_cache, + ) + + if use_background_health_checks: + asyncio.create_task( + _run_background_health_check() + ) # start the background health check coroutine. + + if prompt_injection_detection_obj is not None: # [TODO] - REFACTOR THIS + prompt_injection_detection_obj.update_environment(router=llm_router) + + verbose_proxy_logger.debug("prisma_client: %s", prisma_client) + if prisma_client is not None: + await prisma_client.connect() + + if prisma_client is not None and master_key is not None: + ProxyStartupEvent._add_master_key_hash_to_db( + master_key=master_key, + prisma_client=prisma_client, + litellm_proxy_admin_name=litellm_proxy_admin_name, + general_settings=general_settings, + ) + + if prisma_client is not None and litellm.max_budget > 0: + ProxyStartupEvent._add_proxy_budget_to_db( + litellm_proxy_budget_name=litellm_proxy_admin_name + ) + + ### START BATCH WRITING DB + CHECKING NEW MODELS### + if prisma_client is not None: + await ProxyStartupEvent.initialize_scheduled_background_jobs( + general_settings=general_settings, + prisma_client=prisma_client, + proxy_budget_rescheduler_min_time=proxy_budget_rescheduler_min_time, + proxy_budget_rescheduler_max_time=proxy_budget_rescheduler_max_time, + proxy_batch_write_at=proxy_batch_write_at, + proxy_logging_obj=proxy_logging_obj, + store_model_in_db=store_model_in_db, + ) + #### API ENDPOINTS #### @router.get( diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index d41cf2dfbf..6089bf0c36 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -349,6 +349,31 @@ class ProxyLogging: ) self.premium_user = premium_user + def startup_event( + self, + llm_router: Optional[litellm.Router], + redis_usage_cache: Optional[RedisCache], + ): + """Initialize logging and alerting on proxy startup""" + ## UPDATE SLACK ALERTING ## + self.slack_alerting_instance.update_values(llm_router=llm_router) + + ## UPDATE INTERNAL USAGE CACHE ## + self.update_values( + redis_cache=redis_usage_cache + ) # used by parallel request limiter for rate limiting keys across instances + + self._init_litellm_callbacks( + llm_router=llm_router + ) # INITIALIZE LITELLM CALLBACKS ON SERVER STARTUP <- do this to catch any logging errors on startup, not when calls are being made + + if "daily_reports" in self.slack_alerting_instance.alert_types: + asyncio.create_task( + self.slack_alerting_instance._run_scheduled_daily_report( + llm_router=llm_router + ) + ) # RUN DAILY REPORT (if scheduled) + def update_values( self, alerting: Optional[List] = None, diff --git a/tests/local_testing/test_proxy_server.py b/tests/local_testing/test_proxy_server.py index d76894ce69..e92d84c55d 100644 --- a/tests/local_testing/test_proxy_server.py +++ b/tests/local_testing/test_proxy_server.py @@ -1894,3 +1894,27 @@ async def test_proxy_model_group_info_rerank(prisma_client): # asyncio.run(test()) # except Exception as e: # pytest.fail(f"An exception occurred - {str(e)}") + + +@pytest.mark.asyncio +async def test_proxy_server_prisma_setup(): + from litellm.proxy.proxy_server import ProxyStartupEvent + from litellm.proxy.utils import ProxyLogging + from litellm.caching import DualCache + + user_api_key_cache = DualCache() + + with patch.object( + litellm.proxy.proxy_server, "PrismaClient", new=MagicMock() + ) as mock_prisma_client: + mock_client = mock_prisma_client.return_value # This is the mocked instance + mock_client.check_view_exists = AsyncMock() # Mock the check_view_exists method + + ProxyStartupEvent._setup_prisma_client( + database_url=os.getenv("DATABASE_URL"), + proxy_logging_obj=ProxyLogging(user_api_key_cache=user_api_key_cache), + user_api_key_cache=user_api_key_cache, + ) + + await asyncio.sleep(1) + mock_client.check_view_exists.assert_called_once() From 9fccf829b12a678b01975dd29c754b9e951b24cf Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 24 Oct 2024 22:03:16 -0700 Subject: [PATCH 36/62] =?UTF-8?q?feat(litellm=5Fpre=5Fcall=5Futils.py):=20?= =?UTF-8?q?support=20'add=5Fuser=5Finformation=5Fto=5Fllm=E2=80=A6=20(#639?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(litellm_pre_call_utils.py): support 'add_user_information_to_llm_headers' param enables passing user info to backend llm (user request for custom vllm server) * fix(litellm_logging.py): fix linting error --- litellm/__init__.py | 3 + litellm/litellm_core_utils/litellm_logging.py | 46 +++++ litellm/proxy/litellm_pre_call_utils.py | 180 ++++++++++++------ litellm/types/utils.py | 16 +- tests/local_testing/test_proxy_server.py | 6 +- tests/local_testing/test_proxy_utils.py | 43 ++++- 6 files changed, 221 insertions(+), 73 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 11b34f5048..3282660e98 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -80,6 +80,9 @@ turn_off_message_logging: Optional[bool] = False log_raw_request_response: bool = False redact_messages_in_exceptions: Optional[bool] = False redact_user_api_key_info: Optional[bool] = False +add_user_information_to_llm_headers: Optional[bool] = ( + None # adds user_id, team_id, token hash (params from StandardLoggingMetadata) to request headers +) store_audit_logs = False # Enterprise feature, allow users to see audit logs ## end of callbacks ############# diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index fd73352014..5201bfe1ed 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2798,6 +2798,52 @@ def get_standard_logging_object_payload( return None +def get_standard_logging_metadata( + metadata: Optional[Dict[str, Any]] +) -> StandardLoggingMetadata: + """ + Clean and filter the metadata dictionary to include only the specified keys in StandardLoggingMetadata. + + Args: + metadata (Optional[Dict[str, Any]]): The original metadata dictionary. + + Returns: + StandardLoggingMetadata: A StandardLoggingMetadata object containing the cleaned metadata. + + Note: + - If the input metadata is None or not a dictionary, an empty StandardLoggingMetadata object is returned. + - If 'user_api_key' is present in metadata and is a valid SHA256 hash, it's stored as 'user_api_key_hash'. + """ + # Initialize with default values + clean_metadata = StandardLoggingMetadata( + user_api_key_hash=None, + user_api_key_alias=None, + user_api_key_team_id=None, + user_api_key_org_id=None, + user_api_key_user_id=None, + user_api_key_team_alias=None, + spend_logs_metadata=None, + requester_ip_address=None, + requester_metadata=None, + ) + if isinstance(metadata, dict): + # Filter the metadata dictionary to include only the specified keys + clean_metadata = StandardLoggingMetadata( + **{ # type: ignore + key: metadata[key] + for key in StandardLoggingMetadata.__annotations__.keys() + if key in metadata + } + ) + + if metadata.get("user_api_key") is not None: + if is_valid_sha256_hash(str(metadata.get("user_api_key"))): + clean_metadata["user_api_key_hash"] = metadata.get( + "user_api_key" + ) # this is the hash + return clean_metadata + + def scrub_sensitive_keys_in_metadata(litellm_params: Optional[dict]): if litellm_params is None: litellm_params = {} diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 62f4ce440d..9ee5476529 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -16,7 +16,10 @@ from litellm.proxy._types import ( UserAPIKeyAuth, ) from litellm.proxy.auth.auth_utils import get_request_route -from litellm.types.utils import SupportedCacheControls +from litellm.types.utils import ( + StandardLoggingUserAPIKeyMetadata, + SupportedCacheControls, +) if TYPE_CHECKING: from litellm.proxy.proxy_server import ProxyConfig as _ProxyConfig @@ -159,56 +162,107 @@ def clean_headers( return clean_headers -def get_forwardable_headers( - headers: Union[Headers, dict], -): - """ - Get the headers that should be forwarded to the LLM Provider. - - Looks for any `x-` headers and sends them to the LLM Provider. - """ - forwarded_headers = {} - for header, value in headers.items(): - if header.lower().startswith("x-") and not header.lower().startswith( - "x-stainless" - ): # causes openai sdk to fail - forwarded_headers[header] = value - - return forwarded_headers - - -def get_openai_org_id_from_headers( - headers: dict, general_settings: Optional[Dict] = None -) -> Optional[str]: - """ - Get the OpenAI Org ID from the headers. - """ - if ( - general_settings is not None - and general_settings.get("forward_openai_org_id") is not True +class LiteLLMProxyRequestSetup: + @staticmethod + def _get_forwardable_headers( + headers: Union[Headers, dict], ): + """ + Get the headers that should be forwarded to the LLM Provider. + + Looks for any `x-` headers and sends them to the LLM Provider. + """ + forwarded_headers = {} + for header, value in headers.items(): + if header.lower().startswith("x-") and not header.lower().startswith( + "x-stainless" + ): # causes openai sdk to fail + forwarded_headers[header] = value + + return forwarded_headers + + @staticmethod + def get_openai_org_id_from_headers( + headers: dict, general_settings: Optional[Dict] = None + ) -> Optional[str]: + """ + Get the OpenAI Org ID from the headers. + """ + if ( + general_settings is not None + and general_settings.get("forward_openai_org_id") is not True + ): + return None + for header, value in headers.items(): + if header.lower() == "openai-organization": + return value return None - for header, value in headers.items(): - if header.lower() == "openai-organization": - return value - return None + @staticmethod + def add_headers_to_llm_call( + headers: dict, user_api_key_dict: UserAPIKeyAuth + ) -> dict: + """ + Add headers to the LLM call -def add_litellm_data_for_backend_llm_call( - headers: dict, general_settings: Optional[Dict[str, Any]] = None -) -> LitellmDataForBackendLLMCall: - """ - - Adds forwardable headers - - Adds org id - """ - data = LitellmDataForBackendLLMCall() - _headers = get_forwardable_headers(headers) - if _headers != {}: - data["headers"] = _headers - _organization = get_openai_org_id_from_headers(headers, general_settings) - if _organization is not None: - data["organization"] = _organization - return data + - Checks request headers for forwardable headers + - Checks if user information should be added to the headers + """ + from litellm.litellm_core_utils.litellm_logging import ( + get_standard_logging_metadata, + ) + + returned_headers = LiteLLMProxyRequestSetup._get_forwardable_headers(headers) + + if litellm.add_user_information_to_llm_headers is True: + litellm_logging_metadata_headers = ( + LiteLLMProxyRequestSetup.get_sanitized_user_information_from_key( + user_api_key_dict=user_api_key_dict + ) + ) + for k, v in litellm_logging_metadata_headers.items(): + if v is not None: + returned_headers["x-litellm-{}".format(k)] = v + + return returned_headers + + @staticmethod + def add_litellm_data_for_backend_llm_call( + *, + headers: dict, + user_api_key_dict: UserAPIKeyAuth, + general_settings: Optional[Dict[str, Any]] = None, + ) -> LitellmDataForBackendLLMCall: + """ + - Adds forwardable headers + - Adds org id + """ + data = LitellmDataForBackendLLMCall() + _headers = LiteLLMProxyRequestSetup.add_headers_to_llm_call( + headers, user_api_key_dict + ) + if _headers != {}: + data["headers"] = _headers + _organization = LiteLLMProxyRequestSetup.get_openai_org_id_from_headers( + headers, general_settings + ) + if _organization is not None: + data["organization"] = _organization + return data + + @staticmethod + def get_sanitized_user_information_from_key( + user_api_key_dict: UserAPIKeyAuth, + ) -> StandardLoggingUserAPIKeyMetadata: + user_api_key_logged_metadata = StandardLoggingUserAPIKeyMetadata( + user_api_key_hash=user_api_key_dict.api_key, # just the hashed token + user_api_key_alias=user_api_key_dict.key_alias, + user_api_key_team_id=user_api_key_dict.team_id, + user_api_key_user_id=user_api_key_dict.user_id, + user_api_key_org_id=user_api_key_dict.org_id, + user_api_key_team_alias=user_api_key_dict.team_alias, + ) + return user_api_key_logged_metadata async def add_litellm_data_to_request( # noqa: PLR0915 @@ -246,7 +300,13 @@ async def add_litellm_data_to_request( # noqa: PLR0915 ), ) - data.update(add_litellm_data_for_backend_llm_call(_headers, general_settings)) + data.update( + LiteLLMProxyRequestSetup.add_litellm_data_for_backend_llm_call( + headers=_headers, + user_api_key_dict=user_api_key_dict, + general_settings=general_settings, + ) + ) # Include original request and headers in the data data["proxy_server_request"] = { @@ -294,13 +354,22 @@ async def add_litellm_data_to_request( # noqa: PLR0915 data["metadata"] ) - data[_metadata_variable_name]["user_api_key"] = user_api_key_dict.api_key - data[_metadata_variable_name]["user_api_key_alias"] = getattr( - user_api_key_dict, "key_alias", None + user_api_key_logged_metadata = ( + LiteLLMProxyRequestSetup.get_sanitized_user_information_from_key( + user_api_key_dict=user_api_key_dict + ) ) + data[_metadata_variable_name].update(user_api_key_logged_metadata) + data[_metadata_variable_name][ + "user_api_key" + ] = ( + user_api_key_dict.api_key + ) # this is just the hashed token. [TODO]: replace variable name in repo. + data[_metadata_variable_name]["user_api_end_user_max_budget"] = getattr( user_api_key_dict, "end_user_max_budget", None ) + data[_metadata_variable_name]["litellm_api_version"] = version if general_settings is not None: @@ -308,15 +377,6 @@ async def add_litellm_data_to_request( # noqa: PLR0915 general_settings.get("global_max_parallel_requests", None) ) - data[_metadata_variable_name]["user_api_key_user_id"] = user_api_key_dict.user_id - data[_metadata_variable_name]["user_api_key_org_id"] = user_api_key_dict.org_id - data[_metadata_variable_name]["user_api_key_team_id"] = getattr( - user_api_key_dict, "team_id", None - ) - data[_metadata_variable_name]["user_api_key_team_alias"] = getattr( - user_api_key_dict, "team_alias", None - ) - ### KEY-LEVEL Controls key_metadata = user_api_key_dict.metadata if "cache" in key_metadata: diff --git a/litellm/types/utils.py b/litellm/types/utils.py index 8cc0844b31..0b7a29c91b 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -1412,16 +1412,20 @@ class AdapterCompletionStreamWrapper: raise StopAsyncIteration -class StandardLoggingMetadata(TypedDict): +class StandardLoggingUserAPIKeyMetadata(TypedDict): + user_api_key_hash: Optional[str] # hash of the litellm virtual key used + user_api_key_alias: Optional[str] + user_api_key_org_id: Optional[str] + user_api_key_team_id: Optional[str] + user_api_key_user_id: Optional[str] + user_api_key_team_alias: Optional[str] + + +class StandardLoggingMetadata(StandardLoggingUserAPIKeyMetadata): """ Specific metadata k,v pairs logged to integration for easier cost tracking """ - user_api_key_hash: Optional[str] # hash of the litellm virtual key used - user_api_key_alias: Optional[str] - user_api_key_team_id: Optional[str] - user_api_key_user_id: Optional[str] - user_api_key_team_alias: Optional[str] spend_logs_metadata: Optional[ dict ] # special param to log k,v pairs to spendlogs for a call diff --git a/tests/local_testing/test_proxy_server.py b/tests/local_testing/test_proxy_server.py index e92d84c55d..8032435577 100644 --- a/tests/local_testing/test_proxy_server.py +++ b/tests/local_testing/test_proxy_server.py @@ -203,7 +203,7 @@ def test_add_headers_to_request(litellm_key_header_name): import json from litellm.proxy.litellm_pre_call_utils import ( clean_headers, - get_forwardable_headers, + LiteLLMProxyRequestSetup, ) headers = { @@ -215,7 +215,9 @@ def test_add_headers_to_request(litellm_key_header_name): request._url = URL(url="/chat/completions") request._body = json.dumps({"model": "gpt-3.5-turbo"}).encode("utf-8") request_headers = clean_headers(headers, litellm_key_header_name) - forwarded_headers = get_forwardable_headers(request_headers) + forwarded_headers = LiteLLMProxyRequestSetup._get_forwardable_headers( + request_headers + ) assert forwarded_headers == {"X-Custom-Header": "Custom-Value"} diff --git a/tests/local_testing/test_proxy_utils.py b/tests/local_testing/test_proxy_utils.py index a74e9e78b3..74ef75392a 100644 --- a/tests/local_testing/test_proxy_utils.py +++ b/tests/local_testing/test_proxy_utils.py @@ -371,12 +371,12 @@ def test_is_request_body_safe_model_enabled( def test_reading_openai_org_id_from_headers(): - from litellm.proxy.litellm_pre_call_utils import get_openai_org_id_from_headers + from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup headers = { "OpenAI-Organization": "test_org_id", } - org_id = get_openai_org_id_from_headers(headers) + org_id = LiteLLMProxyRequestSetup.get_openai_org_id_from_headers(headers) assert org_id == "test_org_id" @@ -399,11 +399,44 @@ def test_reading_openai_org_id_from_headers(): ) def test_add_litellm_data_for_backend_llm_call(headers, expected_data): import json - from litellm.proxy.litellm_pre_call_utils import ( - add_litellm_data_for_backend_llm_call, + from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup + from litellm.proxy._types import UserAPIKeyAuth + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_api_key", user_id="test_user_id", org_id="test_org_id" ) - data = add_litellm_data_for_backend_llm_call(headers) + data = LiteLLMProxyRequestSetup.add_litellm_data_for_backend_llm_call( + headers=headers, + user_api_key_dict=user_api_key_dict, + general_settings=None, + ) + + assert json.dumps(data, sort_keys=True) == json.dumps(expected_data, sort_keys=True) + + +def test_foward_litellm_user_info_to_backend_llm_call(): + import json + + litellm.add_user_information_to_llm_headers = True + + from litellm.proxy.litellm_pre_call_utils import LiteLLMProxyRequestSetup + from litellm.proxy._types import UserAPIKeyAuth + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_api_key", user_id="test_user_id", org_id="test_org_id" + ) + + data = LiteLLMProxyRequestSetup.add_headers_to_llm_call( + headers={}, + user_api_key_dict=user_api_key_dict, + ) + + expected_data = { + "x-litellm-user_api_key_user_id": "test_user_id", + "x-litellm-user_api_key_org_id": "test_org_id", + "x-litellm-user_api_key_hash": "test_api_key", + } assert json.dumps(data, sort_keys=True) == json.dumps(expected_data, sort_keys=True) From 2d2a2c35d8667c65e78837653918af38059a57c2 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 09:23:08 +0400 Subject: [PATCH 37/62] ui show created at date --- .../src/components/view_key_table.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/litellm-dashboard/src/components/view_key_table.tsx b/ui/litellm-dashboard/src/components/view_key_table.tsx index 0595e31fd6..474a308e9f 100644 --- a/ui/litellm-dashboard/src/components/view_key_table.tsx +++ b/ui/litellm-dashboard/src/components/view_key_table.tsx @@ -802,6 +802,7 @@ const ViewKeyTable: React.FC = ({ Key Alias Secret Key + Created Expires Spend (USD) Budget (USD) @@ -843,10 +844,19 @@ const ViewKeyTable: React.FC = ({ {item.key_name} + + {item.created_at != null ? ( +
+

{new Date(item.created_at).toLocaleDateString()}

+
+ ) : ( +

Not available

+ )} +
{item.expires != null ? (
-

{new Date(item.expires).toLocaleString()}

+

{new Date(item.expires).toLocaleDateString()}

) : (

Never

From c4cab8812ae2f78c47326195e8b2e022ffd57175 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:24:40 +0400 Subject: [PATCH 38/62] add key/{token_id}/regenerate to internal user routes --- litellm/proxy/_types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 629e002b56..2da8674392 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -340,6 +340,7 @@ class LiteLLMRoutes(enum.Enum): "/sso/get/ui_settings", "/login", "/key/generate", + "/key/{token_id}/regenerate", "/key/update", "/key/info", "/key/delete", From 2e0f501b562603f766a1d9c3f2ba4089cf989358 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:26:43 +0400 Subject: [PATCH 39/62] use static methods for Routechecks --- litellm/proxy/auth/route_checks.py | 319 ++++++++++++------------ litellm/proxy/auth/user_api_key_auth.py | 4 +- 2 files changed, 163 insertions(+), 160 deletions(-) diff --git a/litellm/proxy/auth/route_checks.py b/litellm/proxy/auth/route_checks.py index cc8fd31136..6687cf0525 100644 --- a/litellm/proxy/auth/route_checks.py +++ b/litellm/proxy/auth/route_checks.py @@ -17,175 +17,178 @@ from .auth_checks_organization import _user_is_org_admin from .auth_utils import _has_user_setup_sso -def non_proxy_admin_allowed_routes_check( - user_obj: Optional[LiteLLM_UserTable], - _user_role: Optional[LitellmUserRoles], - route: str, - request: Request, - valid_token: UserAPIKeyAuth, - api_key: str, - request_data: dict, -): - """ - Checks if Non Proxy Admin User is allowed to access the route - """ +class RouteChecks: - # Check user has defined custom admin routes - custom_admin_only_route_check( - route=route, - ) - - if is_llm_api_route(route=route): - pass - elif ( - route in LiteLLMRoutes.info_routes.value - ): # check if user allowed to call an info route - if route == "/key/info": - # check if user can access this route - query_params = request.query_params - key = query_params.get("key") - if key is not None and hash_token(token=key) != api_key: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="user not allowed to access this key's info", - ) - elif route == "/user/info": - # check if user can access this route - query_params = request.query_params - user_id = query_params.get("user_id") - verbose_proxy_logger.debug( - f"user_id: {user_id} & valid_token.user_id: {valid_token.user_id}" - ) - if user_id and user_id != valid_token.user_id: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail="key not allowed to access this user's info. user_id={}, key's user_id={}".format( - user_id, valid_token.user_id - ), - ) - elif route == "/model/info": - # /model/info just shows models user has access to - pass - elif route == "/team/info": - pass # handled by function itself - elif _has_user_setup_sso() and route in LiteLLMRoutes.sso_only_routes.value: - pass - elif ( - route in LiteLLMRoutes.global_spend_tracking_routes.value - and getattr(valid_token, "permissions", None) is not None - and "get_spend_routes" in getattr(valid_token, "permissions", []) + @staticmethod + def non_proxy_admin_allowed_routes_check( + user_obj: Optional[LiteLLM_UserTable], + _user_role: Optional[LitellmUserRoles], + route: str, + request: Request, + valid_token: UserAPIKeyAuth, + api_key: str, + request_data: dict, ): + """ + Checks if Non Proxy Admin User is allowed to access the route + """ - pass - elif _user_role == LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value: - if is_llm_api_route(route=route): - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"user not allowed to access this OpenAI routes, role= {_user_role}", - ) - if route in LiteLLMRoutes.management_routes.value: - # the Admin Viewer is only allowed to call /user/update for their own user_id and can only update - if route == "/user/update": - - # Check the Request params are valid for PROXY_ADMIN_VIEW_ONLY - if request_data is not None and isinstance(request_data, dict): - _params_updated = request_data.keys() - for param in _params_updated: - if param not in ["user_email", "password"]: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route} and updating invalid param: {param}. only user_email and password can be updated", - ) - else: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route}", - ) - - elif ( - _user_role == LitellmUserRoles.INTERNAL_USER.value - and route in LiteLLMRoutes.internal_user_routes.value - ): - pass - elif ( - _user_is_org_admin(request_data=request_data, user_object=user_obj) - and route in LiteLLMRoutes.org_admin_allowed_routes.value - ): - pass - elif ( - _user_role == LitellmUserRoles.INTERNAL_USER_VIEW_ONLY.value - and route in LiteLLMRoutes.internal_user_view_only_routes.value - ): - pass - elif ( - route in LiteLLMRoutes.self_managed_routes.value - ): # routes that manage their own allowed/disallowed logic - pass - else: - user_role = "unknown" - user_id = "unknown" - if user_obj is not None: - user_role = user_obj.user_role or "unknown" - user_id = user_obj.user_id or "unknown" - raise Exception( - f"Only proxy admin can be used to generate, delete, update info for new keys/users/teams. Route={route}. Your role={user_role}. Your user_id={user_id}" + # Check user has defined custom admin routes + RouteChecks.custom_admin_only_route_check( + route=route, ) + if RouteChecks.is_llm_api_route(route=route): + pass + elif ( + route in LiteLLMRoutes.info_routes.value + ): # check if user allowed to call an info route + if route == "/key/info": + # check if user can access this route + query_params = request.query_params + key = query_params.get("key") + if key is not None and hash_token(token=key) != api_key: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="user not allowed to access this key's info", + ) + elif route == "/user/info": + # check if user can access this route + query_params = request.query_params + user_id = query_params.get("user_id") + verbose_proxy_logger.debug( + f"user_id: {user_id} & valid_token.user_id: {valid_token.user_id}" + ) + if user_id and user_id != valid_token.user_id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="key not allowed to access this user's info. user_id={}, key's user_id={}".format( + user_id, valid_token.user_id + ), + ) + elif route == "/model/info": + # /model/info just shows models user has access to + pass + elif route == "/team/info": + pass # handled by function itself + elif _has_user_setup_sso() and route in LiteLLMRoutes.sso_only_routes.value: + pass + elif ( + route in LiteLLMRoutes.global_spend_tracking_routes.value + and getattr(valid_token, "permissions", None) is not None + and "get_spend_routes" in getattr(valid_token, "permissions", []) + ): -def custom_admin_only_route_check(route: str): - from litellm.proxy.proxy_server import general_settings, premium_user + pass + elif _user_role == LitellmUserRoles.PROXY_ADMIN_VIEW_ONLY.value: + if RouteChecks.is_llm_api_route(route=route): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=f"user not allowed to access this OpenAI routes, role= {_user_role}", + ) + if route in LiteLLMRoutes.management_routes.value: + # the Admin Viewer is only allowed to call /user/update for their own user_id and can only update + if route == "/user/update": - if "admin_only_routes" in general_settings: - if premium_user is not True: - verbose_proxy_logger.error( - f"Trying to use 'admin_only_routes' this is an Enterprise only feature. {CommonProxyErrors.not_premium_user.value}" + # Check the Request params are valid for PROXY_ADMIN_VIEW_ONLY + if request_data is not None and isinstance(request_data, dict): + _params_updated = request_data.keys() + for param in _params_updated: + if param not in ["user_email", "password"]: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route} and updating invalid param: {param}. only user_email and password can be updated", + ) + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=f"user not allowed to access this route, role= {_user_role}. Trying to access: {route}", + ) + + elif ( + _user_role == LitellmUserRoles.INTERNAL_USER.value + and route in LiteLLMRoutes.internal_user_routes.value + ): + pass + elif ( + _user_is_org_admin(request_data=request_data, user_object=user_obj) + and route in LiteLLMRoutes.org_admin_allowed_routes.value + ): + pass + elif ( + _user_role == LitellmUserRoles.INTERNAL_USER_VIEW_ONLY.value + and route in LiteLLMRoutes.internal_user_view_only_routes.value + ): + pass + elif ( + route in LiteLLMRoutes.self_managed_routes.value + ): # routes that manage their own allowed/disallowed logic + pass + else: + user_role = "unknown" + user_id = "unknown" + if user_obj is not None: + user_role = user_obj.user_role or "unknown" + user_id = user_obj.user_id or "unknown" + raise Exception( + f"Only proxy admin can be used to generate, delete, update info for new keys/users/teams. Route={route}. Your role={user_role}. Your user_id={user_id}" ) - return - if route in general_settings["admin_only_routes"]: - raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, - detail=f"user not allowed to access this route. Route={route} is an admin only route", - ) - pass + + @staticmethod + def custom_admin_only_route_check(route: str): + from litellm.proxy.proxy_server import general_settings, premium_user + + if "admin_only_routes" in general_settings: + if premium_user is not True: + verbose_proxy_logger.error( + f"Trying to use 'admin_only_routes' this is an Enterprise only feature. {CommonProxyErrors.not_premium_user.value}" + ) + return + if route in general_settings["admin_only_routes"]: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail=f"user not allowed to access this route. Route={route} is an admin only route", + ) + pass + + @staticmethod + def is_llm_api_route(route: str) -> bool: + """ + Helper to checks if provided route is an OpenAI route -def is_llm_api_route(route: str) -> bool: - """ - Helper to checks if provided route is an OpenAI route + Returns: + - True: if route is an OpenAI route + - False: if route is not an OpenAI route + """ + if route in LiteLLMRoutes.openai_routes.value: + return True - Returns: - - True: if route is an OpenAI route - - False: if route is not an OpenAI route - """ + if route in LiteLLMRoutes.anthropic_routes.value: + return True - if route in LiteLLMRoutes.openai_routes.value: - return True + # fuzzy match routes like "/v1/threads/thread_49EIN5QF32s4mH20M7GFKdlZ" + # Check for routes with placeholders + for openai_route in LiteLLMRoutes.openai_routes.value: + # Replace placeholders with regex pattern + # placeholders are written as "/threads/{thread_id}" + if "{" in openai_route: + pattern = re.sub(r"\{[^}]+\}", r"[^/]+", openai_route) + # Anchor the pattern to match the entire string + pattern = f"^{pattern}$" + if re.match(pattern, route): + return True - if route in LiteLLMRoutes.anthropic_routes.value: - return True - - # fuzzy match routes like "/v1/threads/thread_49EIN5QF32s4mH20M7GFKdlZ" - # Check for routes with placeholders - for openai_route in LiteLLMRoutes.openai_routes.value: - # Replace placeholders with regex pattern - # placeholders are written as "/threads/{thread_id}" - if "{" in openai_route: - pattern = re.sub(r"\{[^}]+\}", r"[^/]+", openai_route) - # Anchor the pattern to match the entire string - pattern = f"^{pattern}$" - if re.match(pattern, route): - return True - - # Pass through Bedrock, VertexAI, and Cohere Routes - if "/bedrock/" in route: - return True - if "/vertex-ai/" in route: - return True - if "/gemini/" in route: - return True - if "/cohere/" in route: - return True - if "/langfuse/" in route: - return True - return False + # Pass through Bedrock, VertexAI, and Cohere Routes + if "/bedrock/" in route: + return True + if "/vertex-ai/" in route: + return True + if "/gemini/" in route: + return True + if "/cohere/" in route: + return True + if "/langfuse/" in route: + return True + return False diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index 20b262d317..a41431cb85 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -69,7 +69,7 @@ from litellm.proxy.auth.auth_utils import ( ) from litellm.proxy.auth.oauth2_check import check_oauth2_token from litellm.proxy.auth.oauth2_proxy_hook import handle_oauth2_proxy_request -from litellm.proxy.auth.route_checks import non_proxy_admin_allowed_routes_check +from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.auth.service_account_checks import service_account_checks from litellm.proxy.common_utils.http_parsing_utils import _read_request_body from litellm.proxy.utils import _to_ns @@ -150,7 +150,7 @@ def _is_api_route_allowed( raise Exception("Invalid proxy server token passed") if not _is_user_proxy_admin(user_obj=user_obj): # if non-admin - non_proxy_admin_allowed_routes_check( + RouteChecks.non_proxy_admin_allowed_routes_check( user_obj=user_obj, _user_role=_user_role, route=route, From cdb94ffe16fad234492c944bd204851dc8588fd1 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:31:21 +0400 Subject: [PATCH 40/62] use helper for _route_matches_pattern --- litellm/proxy/auth/route_checks.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/auth/route_checks.py b/litellm/proxy/auth/route_checks.py index 6687cf0525..a237b0bdd6 100644 --- a/litellm/proxy/auth/route_checks.py +++ b/litellm/proxy/auth/route_checks.py @@ -174,10 +174,9 @@ class RouteChecks: # Replace placeholders with regex pattern # placeholders are written as "/threads/{thread_id}" if "{" in openai_route: - pattern = re.sub(r"\{[^}]+\}", r"[^/]+", openai_route) - # Anchor the pattern to match the entire string - pattern = f"^{pattern}$" - if re.match(pattern, route): + if RouteChecks._route_matches_pattern( + route=route, pattern=openai_route + ): return True # Pass through Bedrock, VertexAI, and Cohere Routes @@ -192,3 +191,25 @@ class RouteChecks: if "/langfuse/" in route: return True return False + + @staticmethod + def _route_matches_pattern(route: str, pattern: str) -> bool: + """ + Check if route matches the pattern placed in proxy/_types.py + + Example: + - pattern: "/threads/{thread_id}" + - route: "/threads/thread_49EIN5QF32s4mH20M7GFKdlZ" + - returns: True + + + - pattern: "/key/{token_id}/regenerate" + - route: "/key/regenerate/82akk800000000jjsk" + - returns: False, pattern is "/key/{token_id}/regenerate" + """ + pattern = re.sub(r"\{[^}]+\}", r"[^/]+", pattern) + # Anchor the pattern to match the entire string + pattern = f"^{pattern}$" + if re.match(pattern, route): + return True + return False From 574f07d78259f331a8e145afb074d9875244df26 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:37:11 +0400 Subject: [PATCH 41/62] test_is_ui_route_allowed --- litellm/proxy/auth/auth_checks.py | 8 +-- litellm/proxy/auth/user_api_key_auth.py | 5 ++ tests/local_testing/test_user_api_key_auth.py | 58 +++++++++++++------ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index 940c74b92c..87a7b9ce27 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -28,7 +28,7 @@ from litellm.proxy._types import ( LitellmUserRoles, UserAPIKeyAuth, ) -from litellm.proxy.auth.route_checks import is_llm_api_route +from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.utils import PrismaClient, ProxyLogging, log_to_opentelemetry from litellm.types.services import ServiceLoggerPayload, ServiceTypes @@ -138,7 +138,7 @@ def common_checks( # noqa: PLR0915 general_settings.get("enforce_user_param", None) is not None and general_settings["enforce_user_param"] is True ): - if is_llm_api_route(route=route) and "user" not in request_body: + if RouteChecks.is_llm_api_route(route=route) and "user" not in request_body: raise Exception( f"'user' param not passed in. 'enforce_user_param'={general_settings['enforce_user_param']}" ) @@ -154,7 +154,7 @@ def common_checks( # noqa: PLR0915 + CommonProxyErrors.not_premium_user.value ) - if is_llm_api_route(route=route): + if RouteChecks.is_llm_api_route(route=route): # loop through each enforced param # example enforced_params ['user', 'metadata', 'metadata.generation_name'] for enforced_param in general_settings["enforced_params"]: @@ -182,7 +182,7 @@ def common_checks( # noqa: PLR0915 and global_proxy_spend is not None # only run global budget checks for OpenAI routes # Reason - the Admin UI should continue working if the proxy crosses it's global budget - and is_llm_api_route(route=route) + and RouteChecks.is_llm_api_route(route=route) and route != "/v1/models" and route != "/models" ): diff --git a/litellm/proxy/auth/user_api_key_auth.py b/litellm/proxy/auth/user_api_key_auth.py index a41431cb85..bbdddeee9b 100644 --- a/litellm/proxy/auth/user_api_key_auth.py +++ b/litellm/proxy/auth/user_api_key_auth.py @@ -122,6 +122,11 @@ def _is_ui_route_allowed( ): # Do something if the current route starts with any of the allowed routes return True + elif any( + RouteChecks._route_matches_pattern(route=route, pattern=allowed_route) + for allowed_route in allowed_routes + ): + return True else: if user_obj is not None and _is_user_proxy_admin(user_obj=user_obj): return True diff --git a/tests/local_testing/test_user_api_key_auth.py b/tests/local_testing/test_user_api_key_auth.py index 47f96ccf22..1baddc7dd8 100644 --- a/tests/local_testing/test_user_api_key_auth.py +++ b/tests/local_testing/test_user_api_key_auth.py @@ -293,26 +293,50 @@ async def test_auth_with_allowed_routes(route, should_raise_error): setattr(proxy_server, "general_settings", initial_general_settings) -@pytest.mark.parametrize("route", ["/global/spend/logs", "/key/delete"]) -def test_is_ui_route_allowed(route): +@pytest.mark.parametrize( + "route, user_role, expected_result", + [ + # Proxy Admin checks + ("/global/spend/logs", "proxy_admin", True), + ("/key/delete", "proxy_admin", True), + ("/key/generate", "proxy_admin", True), + ("/key/regenerate", "proxy_admin", True), + # Internal User checks - allowed routes + ("/global/spend/logs", "internal_user", True), + ("/key/delete", "internal_user", True), + ("/key/generate", "internal_user", True), + ("/key/82akk800000000jjsk/regenerate", "internal_user", True), + # Internal User checks - disallowed routes + ("/organization/member_add", "internal_user", False), + ], +) +def test_is_ui_route_allowed(route, user_role, expected_result): from litellm.proxy.auth.user_api_key_auth import _is_ui_route_allowed from litellm.proxy._types import LiteLLM_UserTable + user_obj = LiteLLM_UserTable( + user_id="3b803c0e-666e-4e99-bd5c-6e534c07e297", + max_budget=None, + spend=0.0, + model_max_budget={}, + model_spend={}, + user_email="my-test-email@1234.com", + models=[], + tpm_limit=None, + rpm_limit=None, + user_role=user_role, + organization_memberships=[], + ) + received_args: dict = { "route": route, - "user_obj": LiteLLM_UserTable( - user_id="3b803c0e-666e-4e99-bd5c-6e534c07e297", - max_budget=None, - spend=0.0, - model_max_budget={}, - model_spend={}, - user_email="my-test-email@1234.com", - models=[], - tpm_limit=None, - rpm_limit=None, - user_role="internal_user", - organization_memberships=[], - ), + "user_obj": user_obj, } - - assert _is_ui_route_allowed(**received_args) + try: + assert _is_ui_route_allowed(**received_args) == expected_result + except Exception as e: + # If expected result is False, we expect an error + if expected_result is False: + pass + else: + raise e From c42ec81b8dea36299d705e03446b6a2031d9c00e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:44:14 +0400 Subject: [PATCH 42/62] fix name of tests on config --- .circleci/config.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6bbc95b5a4..8fcf51376c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,7 +119,7 @@ jobs: paths: - local_testing_coverage.xml - local_testing_coverage - ui_endpoint_testing: + auth_ui_unit_tests: docker: - image: cimg/python:3.11 auth: @@ -161,8 +161,8 @@ jobs: - run: name: Rename the coverage files command: | - mv coverage.xml ui_endpoint_testing_coverage.xml - mv .coverage ui_endpoint_testing_coverage + mv coverage.xml auth_ui_unit_tests_coverage.xml + mv .coverage auth_ui_unit_tests_coverage # Store test results - store_test_results: @@ -171,8 +171,8 @@ jobs: - persist_to_workspace: root: . paths: - - ui_endpoint_testing_coverage.xml - - ui_endpoint_testing_coverage + - auth_ui_unit_tests_coverage.xml + - auth_ui_unit_tests_coverage litellm_router_testing: # Runs all tests with the "router" keyword docker: - image: cimg/python:3.11 @@ -813,7 +813,7 @@ jobs: python -m venv venv . venv/bin/activate pip install coverage - coverage combine llm_translation_coverage logging_coverage litellm_router_coverage local_testing_coverage litellm_assistants_api_coverage ui_endpoint_testing_coverage + coverage combine llm_translation_coverage logging_coverage litellm_router_coverage local_testing_coverage litellm_assistants_api_coverage auth_ui_unit_tests_coverage coverage xml - codecov/upload: file: ./coverage.xml @@ -1013,7 +1013,7 @@ workflows: only: - main - /litellm_.*/ - - ui_endpoint_testing: + - auth_ui_unit_tests: filters: branches: only: @@ -1062,7 +1062,7 @@ workflows: - litellm_router_testing - local_testing - litellm_assistants_api_testing - - ui_endpoint_testing + - auth_ui_unit_tests - db_migration_disable_update_check: filters: branches: @@ -1090,7 +1090,7 @@ workflows: - logging_testing - litellm_router_testing - litellm_assistants_api_testing - - ui_endpoint_testing + - auth_ui_unit_tests - db_migration_disable_update_check - e2e_ui_testing - installing_litellm_on_python From 7db8d8b285b583d7cbf8302a7810d26ed8ecbf8a Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:44:26 +0400 Subject: [PATCH 43/62] unit test route checks --- .../test_route_check_unit_tests.py | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/proxy_admin_ui_tests/test_route_check_unit_tests.py diff --git a/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py b/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py new file mode 100644 index 0000000000..edea5f3fed --- /dev/null +++ b/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py @@ -0,0 +1,95 @@ +import os +import sys +import traceback +import uuid +import datetime as dt +from datetime import datetime + +from dotenv import load_dotenv +from fastapi import Request +from fastapi.routing import APIRoute + +load_dotenv() +import io +import os +import time + +# this file is to test litellm/proxy + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system path +import asyncio +import logging + +from fastapi import HTTPException, Request +import pytest +from litellm.proxy.auth.route_checks import RouteChecks +from litellm.proxy._types import LiteLLM_UserTable, LitellmUserRoles, UserAPIKeyAuth + + +# Mock objects and functions +class MockRequest: + def __init__(self, query_params=None): + self.query_params = query_params or {} + + +# Test is_llm_api_route +def test_is_llm_api_route(): + assert RouteChecks.is_llm_api_route("/v1/chat/completions") is True + assert RouteChecks.is_llm_api_route("/v1/completions") is True + assert RouteChecks.is_llm_api_route("/v1/embeddings") is True + assert RouteChecks.is_llm_api_route("/v1/images/generations") is True + assert RouteChecks.is_llm_api_route("/v1/threads/thread_12345") is True + assert RouteChecks.is_llm_api_route("/bedrock/model/invoke") is True + assert RouteChecks.is_llm_api_route("/vertex-ai/text") is True + assert RouteChecks.is_llm_api_route("/gemini/generate") is True + assert RouteChecks.is_llm_api_route("/cohere/generate") is True + + # check non-matching routes + assert RouteChecks.is_llm_api_route("/some/random/route") is False + assert RouteChecks.is_llm_api_route("/key/regenerate/82akk800000000jjsk") is False + assert RouteChecks.is_llm_api_route("/key/82akk800000000jjsk/delete") is False + + +# Test _route_matches_pattern +def test_route_matches_pattern(): + # check matching routes + assert ( + RouteChecks._route_matches_pattern( + "/threads/thread_12345", "/threads/{thread_id}" + ) + is True + ) + assert ( + RouteChecks._route_matches_pattern( + "/key/regenerate/82akk800000000jjsk", "/key/{token_id}/regenerate" + ) + is False + ) + assert ( + RouteChecks._route_matches_pattern( + "/v1/chat/completions", "/v1/chat/completions" + ) + is True + ) + assert ( + RouteChecks._route_matches_pattern( + "/v1/models/gpt-4", "/v1/models/{model_name}" + ) + is True + ) + + # check non-matching routes + assert ( + RouteChecks._route_matches_pattern( + "/v1/chat/completionz/thread_12345", "/v1/chat/completions/{thread_id}" + ) + is False + ) + assert ( + RouteChecks._route_matches_pattern( + "/v1/{thread_id}/messages", "/v1/messages/thread_2345" + ) + is False + ) From 99c721136bde06f9cb9c37a5c363fcb83afe3a36 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:48:00 +0400 Subject: [PATCH 44/62] fix RouteChecks test --- tests/local_testing/test_proxy_routes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/local_testing/test_proxy_routes.py b/tests/local_testing/test_proxy_routes.py index 41ea0e1b4b..31ff7d2ede 100644 --- a/tests/local_testing/test_proxy_routes.py +++ b/tests/local_testing/test_proxy_routes.py @@ -22,7 +22,7 @@ from starlette.datastructures import URL, Headers, QueryParams import litellm from litellm.proxy._types import LiteLLMRoutes from litellm.proxy.auth.auth_utils import get_request_route -from litellm.proxy.auth.route_checks import is_llm_api_route +from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy.proxy_server import app # Configure logging @@ -84,7 +84,7 @@ def test_routes_on_litellm_proxy(): ], ) def test_is_llm_api_route(route: str, expected: bool): - assert is_llm_api_route(route) == expected + assert RouteChecks.is_llm_api_route(route) == expected # Test-case for routes that are similar but should return False @@ -98,12 +98,12 @@ def test_is_llm_api_route(route: str, expected: bool): ], ) def test_is_llm_api_route_similar_but_false(route: str): - assert is_llm_api_route(route) == False + assert RouteChecks.is_llm_api_route(route) is False def test_anthropic_api_routes(): # allow non proxy admins to call anthropic api routes - assert is_llm_api_route(route="/v1/messages") is True + assert RouteChecks.is_llm_api_route(route="/v1/messages") is True def create_request(path: str, base_url: str = "http://testserver") -> Request: From 7c4c3a2ced8cd48c826b6e03e03dd5d5f254e45c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 10:55:54 +0400 Subject: [PATCH 45/62] fix typing on StandardLoggingMetadata --- litellm/litellm_core_utils/litellm_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 5201bfe1ed..206cb235e7 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2515,6 +2515,7 @@ class StandardLoggingPayloadSetup: user_api_key_hash=None, user_api_key_alias=None, user_api_key_team_id=None, + user_api_key_org_id=None, user_api_key_user_id=None, user_api_key_team_alias=None, spend_logs_metadata=None, From 646f4c452486477ae17e139ddf9043cfbaf657e8 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 13:04:37 +0400 Subject: [PATCH 46/62] add unit testing for non_proxy_admin_allowed_routes_check --- .../test_route_check_unit_tests.py | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py b/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py index edea5f3fed..9a4ec84671 100644 --- a/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py +++ b/tests/proxy_admin_ui_tests/test_route_check_unit_tests.py @@ -14,6 +14,7 @@ import io import os import time + # this file is to test litellm/proxy sys.path.insert( @@ -27,6 +28,9 @@ import pytest from litellm.proxy.auth.route_checks import RouteChecks from litellm.proxy._types import LiteLLM_UserTable, LitellmUserRoles, UserAPIKeyAuth +# Replace the actual hash_token function with our mock +import litellm.proxy.auth.route_checks + # Mock objects and functions class MockRequest: @@ -34,6 +38,13 @@ class MockRequest: self.query_params = query_params or {} +def mock_hash_token(token): + return token + + +litellm.proxy.auth.route_checks.hash_token = mock_hash_token + + # Test is_llm_api_route def test_is_llm_api_route(): assert RouteChecks.is_llm_api_route("/v1/chat/completions") is True @@ -93,3 +104,96 @@ def test_route_matches_pattern(): ) is False ) + + +@pytest.fixture +def route_checks(): + return RouteChecks() + + +def test_llm_api_route(route_checks): + """ + Internal User is allowed to access all LLM API routes + """ + assert ( + route_checks.non_proxy_admin_allowed_routes_check( + user_obj=None, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/v1/chat/completions", + request=MockRequest(), + valid_token=UserAPIKeyAuth(api_key="test_key"), + api_key="test_key", + request_data={}, + ) + is None + ) + + +def test_key_info_route_allowed(route_checks): + """ + Internal User is allowed to access /key/info route + """ + assert ( + route_checks.non_proxy_admin_allowed_routes_check( + user_obj=None, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/key/info", + request=MockRequest(query_params={"key": "test_key"}), + valid_token=UserAPIKeyAuth(api_key="test_key"), + api_key="test_key", + request_data={}, + ) + is None + ) + + +def test_key_info_route_forbidden(route_checks): + """ + Internal User is not allowed to access /key/info route for a key they're not using in Authenticated API Key + """ + with pytest.raises(HTTPException) as exc_info: + route_checks.non_proxy_admin_allowed_routes_check( + user_obj=None, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/key/info", + request=MockRequest(query_params={"key": "wrong_key"}), + valid_token=UserAPIKeyAuth(api_key="test_key"), + api_key="test_key", + request_data={}, + ) + assert exc_info.value.status_code == 403 + + +def test_user_info_route_allowed(route_checks): + """ + Internal User is allowed to access /user/info route for their own user_id + """ + assert ( + route_checks.non_proxy_admin_allowed_routes_check( + user_obj=None, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/user/info", + request=MockRequest(query_params={"user_id": "test_user"}), + valid_token=UserAPIKeyAuth(api_key="test_key", user_id="test_user"), + api_key="test_key", + request_data={}, + ) + is None + ) + + +def test_user_info_route_forbidden(route_checks): + """ + Internal User is not allowed to access /user/info route for a different user_id + """ + with pytest.raises(HTTPException) as exc_info: + route_checks.non_proxy_admin_allowed_routes_check( + user_obj=None, + _user_role=LitellmUserRoles.INTERNAL_USER.value, + route="/user/info", + request=MockRequest(query_params={"user_id": "wrong_user"}), + valid_token=UserAPIKeyAuth(api_key="test_key", user_id="test_user"), + api_key="test_key", + request_data={}, + ) + assert exc_info.value.status_code == 403 From ab15028850b6b123d36ecbc75dbfbbb389cc73ec Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 13:31:02 +0400 Subject: [PATCH 47/62] use separate file for create_audit_log_for_update --- .../proxy/management_helpers/audit_logs.py | 43 +++++++++++++++++++ litellm/proxy/proxy_server.py | 1 + 2 files changed, 44 insertions(+) create mode 100644 litellm/proxy/management_helpers/audit_logs.py diff --git a/litellm/proxy/management_helpers/audit_logs.py b/litellm/proxy/management_helpers/audit_logs.py new file mode 100644 index 0000000000..27da9911a6 --- /dev/null +++ b/litellm/proxy/management_helpers/audit_logs.py @@ -0,0 +1,43 @@ +""" +Functions to create audit logs for LiteLLM Proxy +""" + +import json + +import litellm +from litellm._logging import verbose_proxy_logger +from litellm.proxy._types import LiteLLM_AuditLogs + + +async def create_audit_log_for_update(request_data: LiteLLM_AuditLogs): + from litellm.proxy.proxy_server import premium_user, prisma_client + + if premium_user is not True: + return + + if litellm.store_audit_logs is not True: + return + if prisma_client is None: + raise Exception("prisma_client is None, no DB connected") + + verbose_proxy_logger.debug("creating audit log for %s", request_data) + + if isinstance(request_data.updated_values, dict): + request_data.updated_values = json.dumps(request_data.updated_values) + + if isinstance(request_data.before_value, dict): + request_data.before_value = json.dumps(request_data.before_value) + + _request_data = request_data.dict(exclude_none=True) + + try: + await prisma_client.db.litellm_auditlog.create( + data={ + **_request_data, # type: ignore + } + ) + except Exception as e: + # [Non-Blocking Exception. Do not allow blocking LLM API call] + verbose_proxy_logger.error(f"Failed Creating audit log {e}") + + return diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index bcdd4e86c2..26ff04d873 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -194,6 +194,7 @@ from litellm.proxy.management_endpoints.team_callback_endpoints import ( ) from litellm.proxy.management_endpoints.team_endpoints import router as team_router from litellm.proxy.management_endpoints.ui_sso import router as ui_sso_router +from litellm.proxy.management_helpers.audit_logs import create_audit_log_for_update from litellm.proxy.openai_files_endpoints.files_endpoints import is_known_model from litellm.proxy.openai_files_endpoints.files_endpoints import ( router as openai_files_router, From b0bf182db9d8e6034f045b6a4a03abef8650bc41 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 16:47:17 +0400 Subject: [PATCH 48/62] fix LitellmTableNames type --- litellm/proxy/_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 629e002b56..743bd66aa8 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -104,7 +104,7 @@ class LitellmUserRoles(str, enum.Enum): return ui_labels.get(self.value, "") -class LitellmTableNames(enum.Enum): +class LitellmTableNames(str, enum.Enum): """ Enum for Table Names used by LiteLLM """ From 61e28ebb3eff34b84617fea96a43a8fe88178c93 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 16:47:35 +0400 Subject: [PATCH 49/62] fix StandardLoggingMetadata with user_api_key_org_id --- litellm/litellm_core_utils/litellm_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 5201bfe1ed..206cb235e7 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2515,6 +2515,7 @@ class StandardLoggingPayloadSetup: user_api_key_hash=None, user_api_key_alias=None, user_api_key_team_id=None, + user_api_key_org_id=None, user_api_key_user_id=None, user_api_key_team_alias=None, spend_logs_metadata=None, From eb24ce25ea6bdd93eb2766afbdf157824222b222 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 16:48:25 +0400 Subject: [PATCH 50/62] fix create_audit_log_for_update --- .../proxy/management_helpers/audit_logs.py | 2 +- litellm/proxy/proxy_config.yaml | 2 +- litellm/proxy/proxy_server.py | 32 ------------------- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/litellm/proxy/management_helpers/audit_logs.py b/litellm/proxy/management_helpers/audit_logs.py index 27da9911a6..b023e90965 100644 --- a/litellm/proxy/management_helpers/audit_logs.py +++ b/litellm/proxy/management_helpers/audit_logs.py @@ -28,7 +28,7 @@ async def create_audit_log_for_update(request_data: LiteLLM_AuditLogs): if isinstance(request_data.before_value, dict): request_data.before_value = json.dumps(request_data.before_value) - _request_data = request_data.dict(exclude_none=True) + _request_data = request_data.model_dump(exclude_none=True) try: await prisma_client.db.litellm_auditlog.create( diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml index 8f22d0d780..18cc262b45 100644 --- a/litellm/proxy/proxy_config.yaml +++ b/litellm/proxy/proxy_config.yaml @@ -13,7 +13,7 @@ general_settings: proxy_batch_write_at: 60 # Batch write spend updates every 60s litellm_settings: - success_callback: ["langfuse"] + store_audit_logs: true # https://docs.litellm.ai/docs/proxy/reliability#default-fallbacks default_fallbacks: ["gpt-4o-2024-08-06", "claude-3-5-sonnet-20240620"] diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 26ff04d873..7918543b7f 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -6434,38 +6434,6 @@ async def list_end_user( return returned_response -async def create_audit_log_for_update(request_data: LiteLLM_AuditLogs): - if premium_user is not True: - return - - if litellm.store_audit_logs is not True: - return - if prisma_client is None: - raise Exception("prisma_client is None, no DB connected") - - verbose_proxy_logger.debug("creating audit log for %s", request_data) - - if isinstance(request_data.updated_values, dict): - request_data.updated_values = json.dumps(request_data.updated_values) - - if isinstance(request_data.before_value, dict): - request_data.before_value = json.dumps(request_data.before_value) - - _request_data = request_data.dict(exclude_none=True) - - try: - await prisma_client.db.litellm_auditlog.create( - data={ - **_request_data, # type: ignore - } - ) - except Exception as e: - # [Non-Blocking Exception. Do not allow blocking LLM API call] - verbose_proxy_logger.error(f"Failed Creating audit log {e}") - - return - - #### BUDGET TABLE MANAGEMENT #### From c27555677e39b0a2d22ed8cbce2986fa5e40dc09 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 16:50:24 +0400 Subject: [PATCH 51/62] fix code quality --- litellm/proxy/proxy_server.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/litellm/proxy/proxy_server.py b/litellm/proxy/proxy_server.py index 7918543b7f..cadb6063c7 100644 --- a/litellm/proxy/proxy_server.py +++ b/litellm/proxy/proxy_server.py @@ -6399,11 +6399,7 @@ async def list_end_user( --header 'Authorization: Bearer sk-1234' ``` """ - from litellm.proxy.proxy_server import ( - create_audit_log_for_update, - litellm_proxy_admin_name, - prisma_client, - ) + from litellm.proxy.proxy_server import litellm_proxy_admin_name, prisma_client if ( user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN From 8cf0191cf48fdbdf3987b7cc7d2eb0f700f8df65 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 17:28:37 +0400 Subject: [PATCH 52/62] unit testing test_create_audit_log_in_db --- tests/local_testing/test_audit_logs_proxy.py | 151 +++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 tests/local_testing/test_audit_logs_proxy.py diff --git a/tests/local_testing/test_audit_logs_proxy.py b/tests/local_testing/test_audit_logs_proxy.py new file mode 100644 index 0000000000..48187e9b25 --- /dev/null +++ b/tests/local_testing/test_audit_logs_proxy.py @@ -0,0 +1,151 @@ +import os +import sys +import traceback +import uuid +from datetime import datetime + +from dotenv import load_dotenv +from fastapi import Request +from fastapi.routing import APIRoute + + +import io +import os +import time + +# this file is to test litellm/proxy + +sys.path.insert( + 0, os.path.abspath("../..") +) # Adds the parent directory to the system path +import asyncio +import logging + +load_dotenv() + +import pytest +import uuid +import litellm +from litellm._logging import verbose_proxy_logger + +from litellm.proxy.proxy_server import ( + LitellmUserRoles, + audio_transcriptions, + chat_completion, + completion, + embeddings, + image_generation, + model_list, + moderations, + new_end_user, + user_api_key_auth, +) + +from litellm.proxy.utils import PrismaClient, ProxyLogging, hash_token, update_spend + +verbose_proxy_logger.setLevel(level=logging.DEBUG) + +from starlette.datastructures import URL + +from litellm.proxy.management_helpers.audit_logs import create_audit_log_for_update +from litellm.proxy._types import LiteLLM_AuditLogs, LitellmTableNames +from litellm.caching.caching import DualCache + +proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache()) +import json + + +@pytest.mark.asyncio +async def test_create_audit_log_for_update_premium_user(): + """ + Basic unit test for create_audit_log_for_update + + Test that the audit log is created when a premium user updates a team + """ + with patch("litellm.proxy.proxy_server.premium_user", True), patch( + "litellm.store_audit_logs", True + ), patch("litellm.proxy.proxy_server.prisma_client") as mock_prisma: + + mock_prisma.db.litellm_auditlog.create = AsyncMock() + + request_data = LiteLLM_AuditLogs( + id="test_id", + updated_at=datetime.now(), + changed_by="test_changed_by", + action="updated", + table_name=LitellmTableNames.TEAM_TABLE_NAME, + object_id="test_object_id", + updated_values=json.dumps({"key": "value"}), + before_value=json.dumps({"old_key": "old_value"}), + ) + + await create_audit_log_for_update(request_data) + + mock_prisma.db.litellm_auditlog.create.assert_called_once_with( + data={ + "id": "test_id", + "updated_at": request_data.updated_at, + "changed_by": request_data.changed_by, + "action": request_data.action, + "table_name": request_data.table_name, + "object_id": request_data.object_id, + "updated_values": request_data.updated_values, + "before_value": request_data.before_value, + } + ) + + +@pytest.fixture +def prisma_client(): + from litellm.proxy.proxy_cli import append_query_params + + ### add connection pool + pool timeout args + params = {"connection_limit": 100, "pool_timeout": 60} + database_url = os.getenv("DATABASE_URL") + modified_url = append_query_params(database_url, params) + os.environ["DATABASE_URL"] = modified_url + + # Assuming PrismaClient is a class that needs to be instantiated + prisma_client = PrismaClient( + database_url=os.environ["DATABASE_URL"], proxy_logging_obj=proxy_logging_obj + ) + + return prisma_client + + +@pytest.mark.asyncio() +async def test_create_audit_log_in_db(prisma_client): + print("prisma client=", prisma_client) + + setattr(litellm.proxy.proxy_server, "prisma_client", prisma_client) + setattr(litellm.proxy.proxy_server, "master_key", "sk-1234") + setattr(litellm.proxy.proxy_server, "premium_user", True) + setattr(litellm, "store_audit_logs", True) + + await litellm.proxy.proxy_server.prisma_client.connect() + audit_log_id = f"audit_log_id_{uuid.uuid4()}" + + # create a audit log for /key/generate + request_data = LiteLLM_AuditLogs( + id=audit_log_id, + updated_at=datetime.now(), + changed_by="test_changed_by", + action="updated", + table_name=LitellmTableNames.TEAM_TABLE_NAME, + object_id="test_object_id", + updated_values=json.dumps({"key": "value"}), + before_value=json.dumps({"old_key": "old_value"}), + ) + + await create_audit_log_for_update(request_data) + + await asyncio.sleep(1) + + # now read the last log from the db + last_log = await prisma_client.db.litellm_auditlog.find_first( + where={"id": audit_log_id} + ) + + assert last_log.id == audit_log_id + + setattr(litellm, "store_audit_logs", False) From 6f1c06f7ae36bc386be4dbe3f1babd57fe4dae50 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 18:21:17 +0400 Subject: [PATCH 53/62] fix test audit logs --- tests/local_testing/test_audit_logs_proxy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/local_testing/test_audit_logs_proxy.py b/tests/local_testing/test_audit_logs_proxy.py index 48187e9b25..275d486700 100644 --- a/tests/local_testing/test_audit_logs_proxy.py +++ b/tests/local_testing/test_audit_logs_proxy.py @@ -50,6 +50,7 @@ from starlette.datastructures import URL from litellm.proxy.management_helpers.audit_logs import create_audit_log_for_update from litellm.proxy._types import LiteLLM_AuditLogs, LitellmTableNames from litellm.caching.caching import DualCache +from unittest.mock import patch, AsyncMock proxy_logging_obj = ProxyLogging(user_api_key_cache=DualCache()) import json From dbf8b8d8348694b33cd884b7580a962fa48a92a9 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 18:23:40 +0400 Subject: [PATCH 54/62] fix type error --- litellm/litellm_core_utils/litellm_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 5201bfe1ed..be47bac117 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2517,6 +2517,7 @@ class StandardLoggingPayloadSetup: user_api_key_team_id=None, user_api_key_user_id=None, user_api_key_team_alias=None, + user_api_key_org_id=None, spend_logs_metadata=None, requester_ip_address=None, requester_metadata=None, From 81157aa13593a02e8d34ec1ef47a777f347a5b68 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 18:43:00 +0400 Subject: [PATCH 55/62] fix linting --- litellm/litellm_core_utils/litellm_logging.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 75aff9e9b9..206cb235e7 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -2518,7 +2518,6 @@ class StandardLoggingPayloadSetup: user_api_key_org_id=None, user_api_key_user_id=None, user_api_key_team_alias=None, - user_api_key_org_id=None, spend_logs_metadata=None, requester_ip_address=None, requester_metadata=None, From b8d91b3f418fd3ea11c2bf909a1ac4cd75f8a899 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 23:38:54 +0400 Subject: [PATCH 56/62] ui new build --- litellm/proxy/_experimental/out/404.html | 1 + .../out/_next/static/chunks/app/page-7b75dc53f1c6e449.js | 1 + .../out/_next/static/chunks/app/page-7c218fb97a2a9817.js | 1 - .../_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 | 2 +- litellm/proxy/_experimental/out/onboarding.html | 1 + litellm/proxy/_experimental/out/onboarding.txt | 2 +- ui/litellm-dashboard/out/404.html | 2 +- .../out/_next/static/chunks/app/page-7b75dc53f1c6e449.js | 1 + .../out/_next/static/chunks/app/page-7c218fb97a2a9817.js | 1 - .../_buildManifest.js | 0 .../_ssgManifest.js | 0 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 | 2 +- ui/litellm-dashboard/out/onboarding.html | 2 +- ui/litellm-dashboard/out/onboarding.txt | 2 +- 22 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 litellm/proxy/_experimental/out/404.html create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-7c218fb97a2a9817.js rename litellm/proxy/_experimental/out/_next/static/{Q5YcBgN0qLD3pcZcx1fRm => ffXp7j1jzMKpweBFKW_w2}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{Q5YcBgN0qLD3pcZcx1fRm => ffXp7j1jzMKpweBFKW_w2}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/model_hub.html create mode 100644 litellm/proxy/_experimental/out/onboarding.html create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-7c218fb97a2a9817.js rename ui/litellm-dashboard/out/_next/static/{Q5YcBgN0qLD3pcZcx1fRm => ffXp7j1jzMKpweBFKW_w2}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{Q5YcBgN0qLD3pcZcx1fRm => ffXp7j1jzMKpweBFKW_w2}/_ssgManifest.js (100%) diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html new file mode 100644 index 0000000000..40924db8de --- /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/app/page-7b75dc53f1c6e449.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js new file mode 100644 index 0000000000..4eeeca15dd --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,s){Promise.resolve().then(s.bind(s,68031))},667:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return f}});var t=s(57437),n=s(2265),a=s(47907),r=s(2179),i=s(18190),o=s(13810),d=s(10384),c=s(46453),m=s(71801),u=s(52273),h=s(42440),x=s(30953),p=s(777),j=s(37963),g=s(60620),Z=s(13565);function f(){let[e]=g.Z.useForm(),l=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));l&&l.split("=")[1]}("token");let s=l.get("invitation_id"),[f,_]=(0,n.useState)(null),[y,b]=(0,n.useState)(""),[v,k]=(0,n.useState)(""),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(""),[A,C]=(0,n.useState)("");return(0,n.useEffect)(()=>{s&&(0,p.W_)(s).then(e=>{let l=e.login_url;console.log("login_url:",l),I(l);let s=e.token,t=(0,j.o)(s);C(s),console.log("decoded:",t),_(t.key),console.log("decoded user email:",t.user_email),k(t.user_email),w(t.user_id)})},[s]),(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,t.jsxs)(o.Z,{children:[(0,t.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,t.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,t.jsx)(m.Z,{children:"Claim your user account to login to Admin UI."}),(0,t.jsx)(i.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,t.jsxs)(c.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,t.jsx)(d.Z,{children:"SSO is under the Enterprise Tirer."}),(0,t.jsx)(d.Z,{children:(0,t.jsx)(r.Z,{variant:"primary",className:"mb-2",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,t.jsxs)(g.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",f,"token:",A,"formValues:",e),f&&A&&(e.user_email=v,S&&s&&(0,p.m_)(f,s,S,e.password).then(e=>{var l;let s="/ui/";s+="?userID="+((null===(l=e.data)||void 0===l?void 0:l.user_id)||e.user_id),document.cookie="token="+A,console.log("redirecting to:",s),window.location.href=s}))},children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.Z.Item,{label:"Email Address",name:"user_email",children:(0,t.jsx)(u.Z,{type:"email",disabled:!0,value:v,defaultValue:v,className:"max-w-md"})}),(0,t.jsx)(g.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,t.jsx)(u.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,t.jsx)("div",{className:"mt-10",children:(0,t.jsx)(Z.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}},68031:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return lZ}});var t,n,a=s(57437),r=s(2265),i=s(47907),o=s(8792),d=s(40491),c=s(65270),m=e=>{let{userID:l,userRole:s,userEmail:t,premiumUser:n,setProxySettings:r,proxySettings:i}=e;console.log("User ID:",l),console.log("userEmail:",t),console.log("premiumUser:",n),console.log=function(){};let m="";console.log("PROXY_settings=",i),i&&i.PROXY_LOGOUT_URL&&void 0!==i.PROXY_LOGOUT_URL&&(m=i.PROXY_LOGOUT_URL),console.log("logoutUrl=",m);let u=[{key:"1",label:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{children:["Role: ",s]}),(0,a.jsxs)("p",{children:["ID: ",l]}),(0,a.jsxs)("p",{children:["Premium User: ",String(n)]})]})},{key:"2",label:(0,a.jsx)("p",{onClick:()=>{document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=m},children:"Logout"})}];return(0,a.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,a.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,a.jsx)("div",{className:"flex flex-col items-center",children:(0,a.jsx)(o.default,{href:"/",children:(0,a.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,a.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,a.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[n?null:(0,a.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Get enterprise license"})}),(0,a.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,a.jsx)(d.Z,{menu:{items:u},children:(0,a.jsx)(c.Z,{children:t||s})})})]})]})},u=s(777),h=s(10384),x=s(46453),p=s(2179),j=s(52273),g=s(26780),Z=s(15595),f=s(6698),_=s(71801),y=s(42440),b=s(42308),v=s(50670),k=s(60620),S=s(80588),w=s(99129),N=s(18559),I=s(44839),A=s(88707),C=s(13565);let{Option:P}=v.default;var T=e=>{let{userID:l,team:s,userRole:t,accessToken:n,data:i,setData:o}=e,[d]=k.Z.useForm(),[c,m]=(0,r.useState)(!1),[T,E]=(0,r.useState)(null),[O,R]=(0,r.useState)(null),[F,M]=(0,r.useState)([]),[D,L]=(0,r.useState)([]),[U,V]=(0,r.useState)("you"),z=()=>{m(!1),d.resetFields()},B=()=>{m(!1),E(null),d.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==n){let e=(await (0,u.So)(n,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),M(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,t]);let q=async e=>{try{var s,t,a;let r=null!==(s=null==e?void 0:e.key_alias)&&void 0!==s?s:"",c=null!==(t=null==e?void 0:e.team_id)&&void 0!==t?t:null;if((null!==(a=null==i?void 0:i.filter(e=>e.team_id===c).map(e=>e.key_alias))&&void 0!==a?a:[]).includes(r))throw Error("Key alias ".concat(r," already exists for team with ID ").concat(c,", please provide another key alias"));if(S.ZP.info("Making API Call"),m(!0),"service_account"===U){let l={};try{l=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}l.service_account_id=e.key_alias,e.metadata=JSON.stringify(l)}let h=await (0,u.wX)(n,l,e);console.log("key create Response:",h),o(e=>e?[...e,h]:[h]),E(h.key),R(h.soft_budget),S.ZP.success("API Key Created"),d.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,r.useEffect)(()=>{L(s&&s.models.length>0?s.models.includes("all-proxy-models")?F:s.models:F)},[s,F]),(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,a.jsx)(w.Z,{title:"Create Key",visible:c,width:800,footer:null,onOk:z,onCancel:B,children:(0,a.jsxs)(k.Z,{form:d,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Owned By",className:"mb-4",children:(0,a.jsxs)(N.ZP.Group,{onChange:e=>V(e.target.value),value:U,children:[(0,a.jsx)(N.ZP,{value:"you",children:"You"}),(0,a.jsx)(N.ZP,{value:"service_account",children:"Service Account"})]})}),(0,a.jsx)(k.Z.Item,{label:"you"===U?"Key Name":"Service Account ID",name:"key_alias",rules:[{required:!0,message:"Please input a ".concat("you"===U?"key name":"service account ID")}],help:"you"===U?"required":"IDs can include letters, numbers, and hyphens",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:s?s.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&d.setFieldsValue({models:["all-team-models"]})},children:[(0,a.jsx)(P,{value:"all-team-models",children:"All Team Models"},"all-team-models"),D.map(e=>(0,a.jsx)(P,{value:e,children:e},e))]})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==s?void 0:s.max_budget)!==null&&(null==s?void 0:s.max_budget)!==void 0?null==s?void 0:s.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.max_budget&&l>s.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(s.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==s?void 0:s.budget_duration)!==null&&(null==s?void 0:s.budget_duration)!==void 0?null==s?void 0:s.budget_duration:"None"),children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==s?void 0:s.tpm_limit)!==null&&(null==s?void 0:s.tpm_limit)!==void 0?null==s?void 0:s.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.tpm_limit&&l>s.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(s.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==s?void 0:s.rpm_limit)!==null&&(null==s?void 0:s.rpm_limit)!==void 0?null==s?void 0:s.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.rpm_limit&&l>s.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(s.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",className:"mt-8",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Key"})})]})}),T&&(0,a.jsx)(w.Z,{visible:c,onOk:z,onCancel:B,footer:null,children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Save your Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:null!=T?(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:T})}),(0,a.jsx)(b.CopyToClipboard,{text:T,onCopy:()=>{S.ZP.success("API Key copied to clipboard")},children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,a.jsx)(_.Z,{children:"Key being created, this might take 30s"})})]})})]})},E=s(66002),O=s(9454),R=s(98941),F=s(63954),M=s(33393),D=s(5),L=s(13810),U=s(61244),V=s(10827),z=s(3851),B=s(2044),q=s(64167),K=s(74480),W=s(7178),H=s(95093),G=s(27166);let{Option:J}=v.default;console.log=function(){};var Y=e=>{let{userID:l,userRole:s,accessToken:t,selectedTeam:n,data:i,setData:o,teams:d,premiumUser:c}=e,[m,g]=(0,r.useState)(!1),[Z,f]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[P,T]=(0,r.useState)(null),[Y,X]=(0,r.useState)(null),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[es,et]=(0,r.useState)(!1),[en,ea]=(0,r.useState)(null),[er,ei]=(0,r.useState)([]),eo=new Set,[ed,ec]=(0,r.useState)(!1),[em,eu]=(0,r.useState)(!1),[eh,ex]=(0,r.useState)(null),[ep,ej]=(0,r.useState)(null),[eg]=k.Z.useForm(),[eZ,ef]=(0,r.useState)(null),[e_,ey]=(0,r.useState)(eo);(0,r.useEffect)(()=>{console.log("in calculateNewExpiryTime for selectedToken",en),(null==ep?void 0:ep.duration)?ef((e=>{if(!e)return null;try{let l;let s=new Date;if(e.endsWith("s"))l=(0,E.Z)(s,{seconds:parseInt(e)});else if(e.endsWith("h"))l=(0,E.Z)(s,{hours:parseInt(e)});else if(e.endsWith("d"))l=(0,E.Z)(s,{days:parseInt(e)});else throw Error("Invalid duration format");return l.toLocaleString("en-US",{year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",hour12:!0})}catch(e){return null}})(ep.duration)):ef(null),console.log("calculateNewExpiryTime:",eZ)},[en,null==ep?void 0:ep.duration]),(0,r.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==t&&null!==s){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),ei(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[t,l,s]);let eb=e=>{ea(e),ec(!0)},ev=async e=>{if(null==t||null==en)return;let l={...en,metadata:e,key:en.token};try{let e=await (0,u.Nc)(t,l);if(console.log("Model limits updated:",e),i){let l=i.map(l=>l.token===en.token?e:l);o(l)}S.ZP.success("Model-specific limits updated successfully")}catch(e){console.error("Error updating model-specific limits:",e),S.ZP.error("Failed to update model-specific limits")}ec(!1),ea(null)};(0,r.useEffect)(()=>{if(d){let e=new Set;d.forEach((l,s)=>{let t=l.team_id;e.add(t)}),ey(e)}},[d]);let ek=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id);let l=null;if(e.budget_duration)switch(e.budget_duration){case"24h":l="daily";break;case"7d":l="weekly";break;case"30d":l="monthly";break;default:l="None"}ea({...e,budget_duration:l}),el(!0)},eS=async e=>{if(null==t)return;let l=e.token;if(e.key=l,e.budget_duration)switch(e.budget_duration){case"daily":e.budget_duration="24h";break;case"weekly":e.budget_duration="7d";break;case"monthly":e.budget_duration="30d"}console.log("handleEditSubmit:",e);let s=await (0,u.Nc)(t,e);console.log("handleEditSubmit: newKeyValues",s),i&&o(i.map(e=>e.token===l?s:e)),S.ZP.success("Key updated successfully"),el(!1),ea(null)},ew=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=i&&(I(e.token),localStorage.removeItem("userData"+l),f(!0))},eN=async()=>{if(null!=N&&null!=i){try{await (0,u.I1)(t,N);let e=i.filter(e=>e.token!==N);o(e)}catch(e){console.error("Error deleting the key:",e)}f(!1),I(null)}},eI=e=>{ea(e),ef(null),eg.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||""}),eu(!0)},eA=(e,l)=>{ej(s=>({...s,[e]:l}))},eC=async()=>{if(!c){S.ZP.error("Regenerate API Key is an Enterprise feature. Please upgrade to use this feature.");return}if(null!=en)try{let e=await eg.validateFields(),l=await (0,u.s0)(t,en.token,e);if(ex(l.key),i){let s=i.map(s=>s.token===(null==en?void 0:en.token)?{...s,key_name:l.key_name,...e}:s);o(s)}eu(!1),eg.resetFields(),S.ZP.success("API Key regenerated successfully")}catch(e){console.error("Error regenerating key:",e),S.ZP.error("Failed to regenerate API Key")}};if(null!=i)return console.log("RERENDER TRIGGERED"),(0,a.jsxs)("div",{children:[(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,a.jsxs)(V.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Key Alias"}),(0,a.jsx)(K.Z,{children:"Secret Key"}),(0,a.jsx)(K.Z,{children:"Created"}),(0,a.jsx)(K.Z,{children:"Expires"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Budget Reset"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"Rate Limits"}),(0,a.jsx)(K.Z,{children:"Rate Limits per model"})]})}),(0,a.jsx)(z.Z,{children:i.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(n){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(e_.has(e.team_id),", selectedTeam id: ").concat(n.team_id)),(null!=n.team_id||null===e.team_id||e_.has(e.team_id))&&e.team_id!=n.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,a.jsx)(_.Z,{children:e.key_alias}):(0,a.jsx)(_.Z,{children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.key_name})}),(0,a.jsx)(B.Z,{children:null!=e.created_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.created_at).toLocaleDateString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Not available"})}),(0,a.jsx)(B.Z,{children:null!=e.expires?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.expires).toLocaleDateString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,a.jsx)(B.Z,{children:null!=e.max_budget?(0,a.jsx)(_.Z,{children:e.max_budget}):(0,a.jsx)(_.Z,{children:"Unlimited"})}),(0,a.jsx)(B.Z,{children:null!=e.budget_reset_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.budget_reset_at).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(a.Fragment,{children:n&&n.models&&n.models.length>0?n.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{size:"xs",onClick:()=>eb(e),children:"Edit Limits"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{onClick:()=>{ea(e),et(!0)},icon:O.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:es,onCancel:()=>{et(!1),ea(null)},footer:null,width:800,children:en&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-8",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(en.spend).toFixed(4)}catch(e){return en.spend}})()})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.max_budget?(0,a.jsxs)(a.Fragment,{children:[en.max_budget,en.budget_duration&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("br",{}),"Budget will be reset at ",en.budget_reset_at?new Date(en.budget_reset_at).toLocaleString():"Never"]})]}):(0,a.jsx)(a.Fragment,{children:"Unlimited"})})})]},e.name),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.expires?(0,a.jsx)(a.Fragment,{children:new Date(en.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,a.jsx)(a.Fragment,{children:"Never"})})})]},e.name)]}),(0,a.jsxs)(L.Z,{className:"my-4",children:[(0,a.jsx)(y.Z,{children:"Token Name"}),(0,a.jsx)(_.Z,{className:"my-1",children:en.key_alias?en.key_alias:en.key_name}),(0,a.jsx)(y.Z,{children:"Token ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.token}),(0,a.jsx)(y.Z,{children:"User ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.user_id}),(0,a.jsx)(y.Z,{children:"Metadata"}),(0,a.jsx)(_.Z,{className:"my-1",children:(0,a.jsxs)("pre",{children:[JSON.stringify(en.metadata)," "]})})]}),(0,a.jsx)(p.Z,{className:"mx-auto flex items-center",onClick:()=>{et(!1),ea(null)},children:"Close"})]})}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>ek(e)}),(0,a.jsx)(U.Z,{onClick:()=>eI(e),icon:F.Z,size:"sm"}),(0,a.jsx)(U.Z,{onClick:()=>ew(e),icon:M.Z,size:"sm"})]})]},e.token)})})]}),Z&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:eN,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{f(!1),I(null)},children:"Cancel"})]})]})]})})]}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:i}=e,[o]=k.Z.useForm(),[c,m]=(0,r.useState)(n),[u,h]=(0,r.useState)([]),[x,p]=(0,r.useState)(!1);return(0,a.jsx)(w.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{o.validateFields().then(e=>{o.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:o,onFinish:eS,initialValues:{...t,budget_duration:t.budget_duration},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let s=l.filter(e=>!c.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!c.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(s)),s.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(s,"Team models: ").concat(c.models)):Promise.resolve()}}],children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(J,{value:"all-team-models",children:"All Team Models"},"all-team-models"),c&&c.models?c.models.includes("all-proxy-models")?er.filter(e=>"all-proxy-models"!==e).map(e=>(0,a.jsx)(J,{value:e,children:e},e)):c.models.map(e=>(0,a.jsx)(J,{value:e,children:e},e)):er.map(e=>(0,a.jsx)(J,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==c?void 0:c.max_budget)!==null&&(null==c?void 0:c.max_budget)!==void 0?null==c?void 0:c.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.max_budget&&l>c.max_budget)throw console.log("keyTeam.max_budget: ".concat(c.max_budget)),Error("Budget cannot exceed team max budget: $".concat(c.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Current Reset Budget: ".concat(t.budget_duration,", budget will be reset: ").concat(t.budget_reset_at?new Date(t.budget_reset_at).toLocaleString():"Never"),children:(0,a.jsxs)(v.default,{placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"daily",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"weekly",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"monthly",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"token",name:"token",hidden:!0}),(0,a.jsx)(k.Z.Item,{label:"Team",name:"team_id",className:"mt-8",help:"the team this key belongs to",children:(0,a.jsx)(H.Z,{value:t.team_alias,children:null==d?void 0:d.map((e,l)=>(0,a.jsx)(G.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"TPM Limit (tokens per minute)",name:"tpm_limit",help:"tpm_limit cannot exceed team tpm_limit ".concat((null==c?void 0:c.tpm_limit)!==null&&(null==c?void 0:c.tpm_limit)!==void 0?null==c?void 0:c.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.tpm_limit&&l>c.tpm_limit)throw console.log("keyTeam.tpm_limit: ".concat(c.tpm_limit)),Error("tpm_limit cannot exceed team max tpm_limit: $".concat(c.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"RPM Limit (requests per minute)",name:"rpm_limit",help:"rpm_limit cannot exceed team max tpm_limit: ".concat((null==c?void 0:c.rpm_limit)!==null&&(null==c?void 0:c.rpm_limit)!==void 0?null==c?void 0:c.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.rpm_limit&&l>c.rpm_limit)throw console.log("keyTeam.rpm_limit: ".concat(c.rpm_limit)),Error("rpm_limit cannot exceed team max rpm_limit: $".concat(c.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:ee,onCancel:()=>{el(!1),ea(null)},token:en,onSubmit:eS}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:n,accessToken:i}=e,[o,d]=(0,r.useState)({}),[c,m]=(0,r.useState)([]),[h,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(t.metadata){let e=t.metadata.model_tpm_limit||{},l=t.metadata.model_rpm_limit||{},s={};Object.keys({...e,...l}).forEach(t=>{s[t]={tpm:e[t]||0,rpm:l[t]||0}}),d(s)}(async()=>{try{let e=await (0,u.AZ)(i,"",""),l=Array.from(new Set(e.data.map(e=>e.model_name)));m(l)}catch(e){console.error("Error fetching model data:",e),S.ZP.error("Failed to fetch available models")}})()},[t,i]);let j=(e,l,s)=>{d(t=>({...t,[e]:{...t[e],[l]:s||0}}))},g=e=>{d(l=>{let{[e]:s,...t}=l;return t})};return(0,a.jsxs)(w.Z,{title:"Edit Model-Specific Limits",visible:l,onCancel:s,footer:null,width:800,children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model"}),(0,a.jsx)(K.Z,{children:"TPM Limit"}),(0,a.jsx)(K.Z,{children:"RPM Limit"}),(0,a.jsx)(K.Z,{children:"Actions"})]})}),(0,a.jsxs)(z.Z,{children:[Object.entries(o).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:l}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.tpm,onChange:e=>j(l,"tpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.rpm,onChange:e=>j(l,"rpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>g(l),children:"Remove"})})]},l)}),null!==h&&(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(v.default,{style:{width:200},placeholder:"Select a model",onChange:e=>{o[e]||d(l=>({...l,[e]:{tpm:0,rpm:0}})),x(null)},value:h||void 0,children:c.filter(e=>!o.hasOwnProperty(e)).map(e=>(0,a.jsx)(J,{value:e,children:e},e))})}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>x(null),children:"Cancel"})})]})]})]}),(0,a.jsx)(p.Z,{onClick:()=>{x("")},disabled:null!==h,children:"Add Limit"})]}),(0,a.jsxs)("div",{className:"flex justify-end space-x-4 mt-6",children:[(0,a.jsx)(p.Z,{onClick:s,children:"Cancel"}),(0,a.jsx)(p.Z,{onClick:()=>{n({...t.metadata,model_tpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.tpm]})),model_rpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.rpm]}))})},children:"Save"})]})]})},{visible:ed,onCancel:()=>ec(!1),token:en,onSubmit:ev,accessToken:t}),(0,a.jsx)(w.Z,{title:"Regenerate API Key",visible:em,onCancel:()=>{eu(!1),eg.resetFields()},footer:[(0,a.jsx)(p.Z,{onClick:()=>{eu(!1),eg.resetFields()},className:"mr-2",children:"Cancel"},"cancel"),(0,a.jsx)(p.Z,{onClick:eC,disabled:!c,children:c?"Regenerate":"Upgrade to Regenerate"},"regenerate")],children:c?(0,a.jsxs)(k.Z,{form:eg,layout:"vertical",onValuesChange:(e,l)=>{"duration"in e&&eA("duration",e.duration)},children:[(0,a.jsx)(k.Z.Item,{name:"key_alias",label:"Key Alias",children:(0,a.jsx)(j.Z,{disabled:!0})}),(0,a.jsx)(k.Z.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,a.jsx)(A.Z,{step:.01,precision:2,style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"duration",label:"Expire Key (eg: 30s, 30h, 30d)",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsxs)("div",{className:"mt-2 text-sm text-gray-500",children:["Current expiry: ",(null==en?void 0:en.expires)!=null?new Date(en.expires).toLocaleString():"Never"]}),eZ&&(0,a.jsxs)("div",{className:"mt-2 text-sm text-green-600",children:["New expiry: ",eZ]})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",children:"Get Free Trial"})})]})}),eh&&(0,a.jsx)(w.Z,{visible:!!eh,onCancel:()=>ex(null),footer:[(0,a.jsx)(p.Z,{onClick:()=>ex(null),children:"Close"},"close")],children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Regenerated Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please replace your old key with the new key generated. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"Key Alias:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:(null==en?void 0:en.key_alias)||"No alias set"})}),(0,a.jsx)(_.Z,{className:"mt-3",children:"New API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:eh})}),(0,a.jsx)(b.CopyToClipboard,{text:eh,onCopy:()=>S.ZP.success("API Key copied to clipboard"),children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]})]})})]})};console.log=function(){};var X=e=>{let{userID:l,userRole:s,accessToken:t,userSpend:n,userMaxBudget:i,selectedTeam:o}=e;console.log("userSpend: ".concat(n));let[d,c]=(0,r.useState)(null!==n?n:0),[m,h]=(0,r.useState)(o?o.max_budget:null);(0,r.useEffect)(()=>{if(o){if("Default Team"===o.team_alias)h(i);else{let e=!1;if(o.team_memberships)for(let s of o.team_memberships)s.user_id===l&&"max_budget"in s.litellm_budget_table&&null!==s.litellm_budget_table.max_budget&&(h(s.litellm_budget_table.max_budget),e=!0);e||h(o.max_budget)}}},[o,i]);let[x,p]=(0,r.useState)([]);(0,r.useEffect)(()=>{let e=async()=>{if(!t||!l||!s)return};(async()=>{try{if(null===l||null===s)return;if(null!==t){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),p(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,t,l]),(0,r.useEffect)(()=>{null!==n&&c(n)},[n]);let j=[];o&&o.models&&(j=o.models),j&&j.includes("all-proxy-models")?(console.log("user models:",x),j=x):j&&j.includes("all-team-models")?j=o.models:j&&0===j.length&&(j=x);let g=void 0!==d?d.toFixed(4):null;return console.log("spend in view user spend: ".concat(d)),(0,a.jsx)("div",{className:"flex items-center",children:(0,a.jsxs)("div",{className:"flex justify-between gap-x-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Total Spend"}),(0,a.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",g]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Max Budget"}),(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null!==m?"$".concat(m," limit"):"No limit"})]})]})})};console.log=function(){};var $=e=>{let{userID:l,userRole:s,selectedTeam:t,accessToken:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==n){let e=(await (0,u.So)(n,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),o(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,s]);let d=[];return t&&t.models&&(d=t.models),d&&d.includes("all-proxy-models")&&(console.log("user models:",i),d=i),(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==t?void 0:t.team_alias}),(null==t?void 0:t.team_id)&&(0,a.jsxs)("p",{className:"text-xs text-gray-400 dark:text-gray-400 font-semibold",children:["Team ID: ",null==t?void 0:t.team_id]})]})})},Q=e=>{let l,{teams:s,setSelectedTeam:t,userRole:n,proxySettings:i,setProxySettings:o,userInfo:d,accessToken:c}=e;console.log("userInfo: ".concat(JSON.stringify(d)));let m={models:(null==d?void 0:d.models)||[],team_id:null,team_alias:"Default Team",max_budget:(null==d?void 0:d.max_budget)||null},h=async()=>{null===i&&c&&o(await (0,u.Dj)(c))};(0,r.useEffect)(()=>{h()},[i]);let[x,p]=(0,r.useState)(m);return console.log("userRole: ".concat(n)),console.log("proxySettings: ".concat(JSON.stringify(i))),l="App User"===n?s:i&&!0===i.DEFAULT_TEAM_DISABLED?s?[...s]:[m]:s?[...s,m]:[m],(0,a.jsxs)("div",{className:"mt-5 mb-5",children:[(0,a.jsx)(y.Z,{children:"Select Team"}),(0,a.jsx)(_.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,a.jsxs)(_.Z,{className:"mt-3 mb-3",children:[(0,a.jsx)("b",{children:"Default Team:"})," If no team_id is set for a key, it will be grouped under here."]}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>t(e),children:e.team_alias},l))}):(0,a.jsxs)(_.Z,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]})},ee=s(667),el=s(37963),es=s(97482);console.log=function(){},console.log("isLocal:",!1);var et=e=>{let{userID:l,userRole:s,teams:t,keys:n,setUserRole:o,userEmail:d,setUserEmail:c,setTeams:m,setKeys:p,premiumUser:j}=e,[g,Z]=(0,r.useState)(null),f=(0,i.useSearchParams)();f.get("viewSpend"),(0,i.useRouter)();let _=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),y=f.get("invitation_id"),[b,v]=(0,r.useState)(null),[k,S]=(0,r.useState)(null),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)(null),C={models:[],team_alias:"Default Team",team_id:null},[P,E]=(0,r.useState)(t?t[0]:C);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(_){let e=(0,el.o)(_);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),v(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"app_user":return"App User";case"internal_user":return"Internal User";case"internal_user_viewer":return"Internal Viewer";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),o(l)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&b&&s&&!n&&!g){let e=sessionStorage.getItem("userModels"+l);e?N(JSON.parse(e)):(async()=>{try{let e=await (0,u.Dj)(b);A(e);let t=await (0,u.Br)(b,l,s,!1,null,null);console.log("received teams in user dashboard: ".concat(Object.keys(t),"; team values: ").concat(Object.entries(t.teams))),Z(t.user_info),console.log("userSpendData: ".concat(JSON.stringify(g))),p(t.keys),m(t.teams);let n=[...t.teams];n.length>0?(console.log("response['teams']: ".concat(n)),E(n[0])):E(C),sessionStorage.setItem("userData"+l,JSON.stringify(t.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(t.user_info));let a=(await (0,u.So)(b,l,s)).data.map(e=>e.id);console.log("available_model_names:",a),N(a),console.log("userModels:",w),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,_,b,n,s]),(0,r.useEffect)(()=>{if(null!==n&&null!=P&&null!==P.team_id){let e=0;for(let l of n)P.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===P.team_id&&(e+=l.spend);S(e)}else if(null!==n){let e=0;for(let l of n)e+=l.spend;S(e)}},[P]),null!=y)return(0,a.jsx)(ee.default,{});if(null==l||null==_){let e="/sso/key/generate";return document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",console.log("Full URL:",e),window.location.href=e,null}if(null==b)return null;if(null==s&&o("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",P),(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)($,{userID:l,userRole:s,selectedTeam:P||null,accessToken:b}),(0,a.jsx)(X,{userID:l,userRole:s,userMaxBudget:(null==g?void 0:g.max_budget)||null,accessToken:b,userSpend:k,selectedTeam:P||null}),(0,a.jsx)(Y,{userID:l,userRole:s,accessToken:b,selectedTeam:P||null,data:n,setData:p,premiumUser:j,teams:t}),(0,a.jsx)(T,{userID:l,team:P||null,userRole:s,accessToken:b,data:n,setData:p},P?P.team_id:null),(0,a.jsx)(Q,{teams:t,setSelectedTeam:E,userRole:s,proxySettings:I,setProxySettings:A,userInfo:g,accessToken:b})]})})})},en=s(49167),ea=s(35087),er=s(92836),ei=s(26734),eo=s(41608),ed=s(32126),ec=s(23682),em=s(47047),eu=s(76628),eh=s(25707),ex=s(44041),ep=s(6180),ej=s(28683),eg=s(38302),eZ=s(66242),ef=s(78578),e_=s(34658),ey=e=>{let{modelID:l,accessToken:s}=e,[t,n]=(0,r.useState)(!1),i=async()=>{try{S.ZP.info("Making API Call"),n(!0);let e=await (0,u.Og)(s,l);console.log("model delete Response:",e),S.ZP.success("Model ".concat(l," deleted successfully")),n(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(U.Z,{onClick:()=>n(!0),icon:M.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:t,onOk:i,okType:"danger",onCancel:()=>n(!1),children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Delete Model"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Model ID: ",(0,a.jsx)("b",{children:l})]})})]})})]})},eb=s(97766),ev=s(46495),ek=s(18190),eS=s(91118),ew=e=>{let{modelMetrics:l,modelMetricsCategories:s,customTooltip:t,premiumUser:n}=e;return n?(0,a.jsx)(eS.Z,{title:"Time to First token (s)",className:"h-72",data:l,index:"date",showLegend:!1,categories:s,colors:["indigo","rose"],connectNulls:!0,customTooltip:t}):(0,a.jsxs)("div",{children:[(0,a.jsx)(ek.Z,{title:"✨ Enterprise Feature",color:"teal",className:"mt-2 mb-4",children:"Enterprise features are available for users with a specific license, please contact LiteLLM to unlock this limitation."}),(0,a.jsx)(p.Z,{variant:"primary",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get in touch"})})]})},eN=e=>{let{fields:l,selectedProvider:s}=e;return 0===l.length?null:(0,a.jsx)(a.Fragment,{children:l.map(e=>(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:e.field_name.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),name:e.field_name,tooltip:e.field_description,className:"mb-2",children:(0,a.jsx)(j.Z,{placeholder:e.field_value,type:"password"})},e.field_name))})},eI=s(67951);let{Title:eA,Link:eC}=es.default;(t=n||(n={})).OpenAI="OpenAI",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Studio",t.Anthropic="Anthropic",t.Google_AI_Studio="Google AI Studio",t.Bedrock="Amazon Bedrock",t.Groq="Groq",t.MistralAI="Mistral AI",t.Deepseek="Deepseek",t.OpenAI_Compatible="OpenAI-Compatible Endpoints (Together AI, etc.)",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.Cohere="Cohere",t.Databricks="Databricks",t.Ollama="Ollama";let eP={OpenAI:"openai",Azure:"azure",Azure_AI_Studio:"azure_ai",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",Groq:"groq",MistralAI:"mistral",Cohere:"cohere_chat",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai",Databricks:"databricks",Deepseek:"deepseek",Ollama:"ollama"},eT={"BadRequestError (400)":"BadRequestErrorRetries","AuthenticationError (401)":"AuthenticationErrorRetries","TimeoutError (408)":"TimeoutErrorRetries","RateLimitError (429)":"RateLimitErrorRetries","ContentPolicyViolationError (400)":"ContentPolicyViolationErrorRetries","InternalServerError (500)":"InternalServerErrorRetries"},eE=async(e,l,s)=>{try{let t=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(t)),console.log("received type of deployments: ".concat(typeof t)),t.forEach(async s=>{console.log("litellm_model: ".concat(s));let t={},n={};t.model=s;let a="";for(let[l,s]of(console.log("formValues add deployment:",e),Object.entries(e)))if(""!==s){if("model_name"==l)a+=s;else if("custom_llm_provider"==l){console.log("custom_llm_provider:",s);let e=eP[s];t.custom_llm_provider=e,console.log("custom_llm_provider mappingResult:",e)}else if("model"==l)continue;else if("base_model"===l)n[l]=s;else if("custom_model_name"===l)t.model=s;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",s);let e={};if(s&&void 0!=s){try{e=JSON.parse(s)}catch(e){throw S.ZP.error("Failed to parse LiteLLM Extra Params: "+e,10),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,s]of Object.entries(e))t[l]=s}}else t[l]=s}let r={model_name:a,litellm_params:t,model_info:n},i=await (0,u.kK)(l,r);console.log("response for model create call: ".concat(i.data))}),s.resetFields()}catch(e){S.ZP.error("Failed to create model: "+e,10)}};var eO=e=>{let l,{accessToken:s,token:t,userRole:i,userID:o,modelData:d={data:[]},keys:c,setModelData:m,premiumUser:h}=e,[g,Z]=(0,r.useState)([]),[f]=k.Z.useForm(),[b,v]=(0,r.useState)(null),[N,I]=(0,r.useState)(""),[P,T]=(0,r.useState)([]),E=Object.values(n).filter(e=>isNaN(Number(e))),[M,J]=(0,r.useState)([]),[Y,X]=(0,r.useState)("OpenAI"),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[et,ek]=(0,r.useState)(!1),[eS,eO]=(0,r.useState)(null),[eR,eF]=(0,r.useState)([]),[eM,eD]=(0,r.useState)([]),[eL,eU]=(0,r.useState)(null),[eV,ez]=(0,r.useState)([]),[eB,eq]=(0,r.useState)([]),[eK,eW]=(0,r.useState)([]),[eH,eG]=(0,r.useState)([]),[eJ,eY]=(0,r.useState)([]),[eX,e$]=(0,r.useState)([]),[eQ,e0]=(0,r.useState)([]),[e1,e2]=(0,r.useState)([]),[e4,e5]=(0,r.useState)([]),[e8,e3]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),[e6,e7]=(0,r.useState)(null),[e9,le]=(0,r.useState)(0),[ll,ls]=(0,r.useState)({}),[lt,ln]=(0,r.useState)([]),[la,lr]=(0,r.useState)(!1),[li,lo]=(0,r.useState)(null),[ld,lc]=(0,r.useState)(null),[lm,lu]=(0,r.useState)([]);(0,r.useEffect)(()=>{lS(eL,e8.from,e8.to)},[li,ld]);let lh=e=>{eO(e),el(!0)},lx=e=>{eO(e),ek(!0)},lp=async e=>{if(console.log("handleEditSubmit:",e),null==s)return;let l={},t=null;for(let[s,n]of(e.input_cost_per_million_tokens&&(e.input_cost_per_token=e.input_cost_per_million_tokens/1e6,delete e.input_cost_per_million_tokens),e.output_cost_per_million_tokens&&(e.output_cost_per_token=e.output_cost_per_million_tokens/1e6,delete e.output_cost_per_million_tokens),Object.entries(e)))"model_id"!==s?l[s]=n:t=n;let n={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",n);try{await (0,u.um)(s,n),S.ZP.success("Model updated successfully, restart server to see updates"),el(!1),eO(null)}catch(e){console.log("Error occurred")}},lj=()=>{I(new Date().toLocaleString())},lg=async()=>{if(!s){console.error("Access token is missing");return}console.log("new modelGroupRetryPolicy:",e6);try{await (0,u.K_)(s,{router_settings:{model_group_retry_policy:e6}}),S.ZP.success("Retry settings saved successfully")}catch(e){console.error("Failed to save retry settings:",e),S.ZP.error("Failed to save retry settings")}};if((0,r.useEffect)(()=>{if(!s||!t||!i||!o)return;let e=async()=>{try{var e,l,t,n,a,r,d,c,h,x,p,j;let g=await (0,u.hy)(s);J(g);let Z=await (0,u.AZ)(s,o,i);console.log("Model data response:",Z.data),m(Z);let f=new Set;for(let e=0;e0&&(y=_[_.length-1],console.log("_initial_model_group:",y)),console.log("selectedModelGroup:",eL);let b=await (0,u.o6)(s,o,i,y,null===(e=e8.from)||void 0===e?void 0:e.toISOString(),null===(l=e8.to)||void 0===l?void 0:l.toISOString(),null==li?void 0:li.token,ld);console.log("Model metrics response:",b),eq(b.data),eW(b.all_api_bases);let v=await (0,u.Rg)(s,y,null===(t=e8.from)||void 0===t?void 0:t.toISOString(),null===(n=e8.to)||void 0===n?void 0:n.toISOString());eG(v.data),eY(v.all_api_bases);let k=await (0,u.N8)(s,o,i,y,null===(a=e8.from)||void 0===a?void 0:a.toISOString(),null===(r=e8.to)||void 0===r?void 0:r.toISOString(),null==li?void 0:li.token,ld);console.log("Model exceptions response:",k),e$(k.data),e0(k.exception_types);let S=await (0,u.fP)(s,o,i,y,null===(d=e8.from)||void 0===d?void 0:d.toISOString(),null===(c=e8.to)||void 0===c?void 0:c.toISOString(),null==li?void 0:li.token,ld),w=await (0,u.n$)(s,null===(h=e8.from)||void 0===h?void 0:h.toISOString().split("T")[0],null===(x=e8.to)||void 0===x?void 0:x.toISOString().split("T")[0],y);ls(w);let N=await (0,u.v9)(s,null===(p=e8.from)||void 0===p?void 0:p.toISOString().split("T")[0],null===(j=e8.to)||void 0===j?void 0:j.toISOString().split("T")[0],y);ln(N),console.log("dailyExceptions:",w),console.log("dailyExceptionsPerDeplyment:",N),console.log("slowResponses:",S),e5(S);let I=await (0,u.j2)(s);lu(null==I?void 0:I.end_users);let A=(await (0,u.BL)(s,o,i)).router_settings;console.log("routerSettingsInfo:",A);let C=A.model_group_retry_policy,P=A.num_retries;console.log("model_group_retry_policy:",C),console.log("default_retries:",P),e7(C),le(P)}catch(e){console.error("There was an error fetching the model data",e)}};s&&t&&i&&o&&e();let l=async()=>{let e=await (0,u.qm)(s);console.log("received model cost map data: ".concat(Object.keys(e))),v(e)};null==b&&l(),lj()},[s,t,i,o,b,N]),!d||!s||!t||!i||!o)return(0,a.jsx)("div",{children:"Loading..."});let lZ=[],lf=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(b)),null!=b&&"object"==typeof b&&e in b)?b[e].litellm_provider:"openai";if(s){let e=s.split("/"),l=e[0];(a=t)||(a=1===e.length?u(s):l)}else a="-";n&&(r=null==n?void 0:n.input_cost_per_token,i=null==n?void 0:n.output_cost_per_token,o=null==n?void 0:n.max_tokens,c=null==n?void 0:n.max_input_tokens),(null==l?void 0:l.litellm_params)&&(m=Object.fromEntries(Object.entries(null==l?void 0:l.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),d.data[e].provider=a,d.data[e].input_cost=r,d.data[e].output_cost=i,d.data[e].litellm_model_name=s,lf.push(a),d.data[e].input_cost&&(d.data[e].input_cost=(1e6*Number(d.data[e].input_cost)).toFixed(2)),d.data[e].output_cost&&(d.data[e].output_cost=(1e6*Number(d.data[e].output_cost)).toFixed(2)),d.data[e].max_tokens=o,d.data[e].max_input_tokens=c,d.data[e].api_base=null==l?void 0:null===(lb=l.litellm_params)||void 0===lb?void 0:lb.api_base,d.data[e].cleanedLitellmParams=m,lZ.push(l.model_name),console.log(d.data[e])}if(d.data&&d.data.length>0&&d.data.sort((e,l)=>e.provider&&l.provider?e.provider.localeCompare(l.provider):e.provider&&!l.provider?-1:!e.provider&&l.provider?1:0),i&&"Admin Viewer"==i){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let lv=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(n).find(l=>n[l]===e);if(l){let e=eP[l];console.log("mappingResult: ".concat(e));let s=[];"object"==typeof b&&(Object.entries(b).forEach(l=>{let[t,n]=l;null!==n&&"object"==typeof n&&"litellm_provider"in n&&(n.litellm_provider===e||n.litellm_provider.includes(e))&&s.push(t)}),"Cohere"==l&&(console.log("adding cohere chat model"),Object.entries(b).forEach(e=>{let[l,t]=e;null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere"===t.litellm_provider&&s.push(l)}))),T(s),console.log("providerModels: ".concat(P))}},lk=async()=>{try{S.ZP.info("Running health check..."),Q("");let e=await (0,u.EY)(s);Q(e)}catch(e){console.error("Error running health check:",e),Q("Error running health check")}},lS=async(e,l,t)=>{if(console.log("Updating model metrics for group:",e),!s||!o||!i||!l||!t)return;console.log("inside updateModelMetrics - startTime:",l,"endTime:",t),eU(e);let n=null==li?void 0:li.token;void 0===n&&(n=null);let a=ld;void 0===a&&(a=null),l.setHours(0),l.setMinutes(0),l.setSeconds(0),t.setHours(23),t.setMinutes(59),t.setSeconds(59);try{let r=await (0,u.o6)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model metrics response:",r),eq(r.data),eW(r.all_api_bases);let d=await (0,u.Rg)(s,e,l.toISOString(),t.toISOString());eG(d.data),eY(d.all_api_bases);let c=await (0,u.N8)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model exceptions response:",c),e$(c.data),e0(c.exception_types);let m=await (0,u.fP)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);if(console.log("slowResponses:",m),e5(m),e){let n=await (0,u.n$)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ls(n);let a=await (0,u.v9)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ln(a)}}catch(e){console.error("Failed to fetch model metrics",e)}},lw=(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Select API Key Name"}),h?(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{lo(e)},children:e.key_alias},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lc(e)},children:e},l))]})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsxs)(G.Z,{value:String(l),disabled:!0,onClick:()=>{lo(e)},children:["✨ ",e.key_alias," (Enterprise only Feature)"]},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsxs)(G.Z,{value:e,disabled:!0,onClick:()=>{lc(e)},children:["✨ ",e," (Enterprise only Feature)"]},l))]})]})]}),lN=e=>{var l,s;let{payload:t,active:n}=e;if(!n||!t)return null;let r=null===(s=t[0])||void 0===s?void 0:null===(l=s.payload)||void 0===l?void 0:l.date,i=t.sort((e,l)=>l.value-e.value);if(i.length>5){let e=i.length-5;(i=i.slice(0,5)).push({dataKey:"".concat(e," other deployments"),value:t.slice(5).reduce((e,l)=>e+l.value,0),color:"gray"})}return(0,a.jsxs)("div",{className:"w-150 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r&&(0,a.jsxs)("p",{className:"text-tremor-content-emphasis mb-2",children:["Date: ",r]}),i.map((e,l)=>{let s=parseFloat(e.value.toFixed(5)),t=0===s&&e.value>0?"<0.00001":s.toFixed(5);return(0,a.jsxs)("div",{className:"flex justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("div",{className:"w-2 h-2 mt-1 rounded-full bg-".concat(e.color,"-500")}),(0,a.jsx)("p",{className:"text-tremor-content",children:e.dataKey})]}),(0,a.jsx)("p",{className:"font-medium text-tremor-content-emphasis text-righ ml-2",children:t})]},l)})]})},lI=e=>"Vertex AI (Anthropic, Gemini, etc.)"===e?"gemini-pro":"Anthropic"==e||"Amazon Bedrock"==e?"claude-3-opus":"Google AI Studio"==e?"gemini-pro":"Azure AI Studio"==e?"azure_ai/command-r-plus":"Azure"==e?"azure/my-deployment":"gpt-3.5-turbo";console.log("selectedProvider: ".concat(Y)),console.log("providerModels.length: ".concat(P.length));let lA=Object.keys(n).find(e=>n[e]===Y);return lA&&(l=M.find(e=>e.name===eP[lA])),(0,a.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(er.Z,{children:"All Models"}),(0,a.jsx)(er.Z,{children:"Add Model"}),(0,a.jsx)(er.Z,{children:(0,a.jsx)("pre",{children:"/health Models"})}),(0,a.jsx)(er.Z,{children:"Model Analytics"}),(0,a.jsx)(er.Z,{children:"Model Retry Settings"})]}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[N&&(0,a.jsxs)(_.Z,{children:["Last Refreshed: ",N]}),(0,a.jsx)(U.Z,{icon:F.Z,variant:"shadow",size:"xs",className:"self-center",onClick:lj})]})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsxs)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||void 0,onValueChange:e=>eU("all"===e?"all":e),value:eL||void 0,children:[(0,a.jsx)(G.Z,{value:"all",children:"All Models"}),eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))]})]}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{style:{maxWidth:"1500px",width:"100%"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Public Model Name"}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Provider"}),(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"LiteLLM Model"}),"Admin"===i&&(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"API Base"}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Input Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Output Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created At":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created At"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created By":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created By"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"50px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Status"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:d.data.filter(e=>"all"===eL||e.model_name===eL||null==eL||""===eL).map((e,l)=>{var t;return(0,a.jsxs)(W.Z,{style:{maxHeight:"1px",minHeight:"1px"},children:[(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.model_name||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.provider||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.litellm_model_name,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.litellm_model_name?e.litellm_model_name:"",children:e&&e.litellm_model_name?e.litellm_model_name.slice(0,20)+(e.litellm_model_name.length>20?"...":""):"-"})})}),"Admin"===i&&(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.api_base,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.api_base?e.api_base:"",children:e&&e.api_base?e.api_base.slice(0,20):"-"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.input_cost?e.input_cost:null!=e.litellm_params.input_cost_per_token&&void 0!=e.litellm_params.input_cost_per_token?(1e6*Number(e.litellm_params.input_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.output_cost?e.output_cost:e.litellm_params.output_cost_per_token?(1e6*Number(e.litellm_params.output_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&((t=e.model_info.created_at)?new Date(t).toLocaleDateString("en-US"):null)||"-"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&e.model_info.created_by||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:e.model_info.db_model?(0,a.jsx)(D.Z,{size:"xs",className:"text-white",children:(0,a.jsx)("p",{className:"text-xs",children:"DB Model"})}):(0,a.jsx)(D.Z,{size:"xs",className:"text-black",children:(0,a.jsx)("p",{className:"text-xs",children:"Config Model"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsxs)(x.Z,{numItems:3,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:O.Z,size:"sm",onClick:()=>lx(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>lh(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(ey,{modelID:e.model_info.id,accessToken:s})})]})})]},l)})})]})})]}),(0,a.jsx)(e=>{let{visible:l,onCancel:s,model:t,onSubmit:n}=e,[r]=k.Z.useForm(),i={},o="",d="";if(t){i=t.litellm_params,o=t.model_name;let e=t.model_info;e&&(d=e.id,console.log("model_id: ".concat(d)),i.model_id=d)}return(0,a.jsx)(w.Z,{title:"Edit Model "+o,visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n(e),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:lp,initialValues:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"organization",name:"organization",tooltip:"OpenAI Organization ID",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"tpm",name:"tpm",tooltip:"int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"rpm",name:"rpm",tooltip:"int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"max_retries",name:"max_retries",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"Input Cost per 1M Tokens",name:"input_cost_per_million_tokens",tooltip:"float (optional) - Input cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"Output Cost per 1M Tokens",name:"output_cost_per_million_tokens",tooltip:"float (optional) - Output cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:ee,onCancel:()=>{el(!1),eO(null)},model:eS,onSubmit:lp}),(0,a.jsxs)(w.Z,{title:eS&&eS.model_name,visible:et,width:800,footer:null,onCancel:()=>{ek(!1),eO(null)},children:[(0,a.jsx)(y.Z,{children:"Model Info"}),(0,a.jsx)(eI.Z,{language:"json",children:eS&&JSON.stringify(eS,null,2)})]})]}),(0,a.jsxs)(ed.Z,{className:"h-full",children:[(0,a.jsx)(eA,{level:2,children:"Add new model"}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(k.Z,{form:f,onFinish:()=>{f.validateFields().then(e=>{eE(e,s,f)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc.",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:Y.toString(),children:E.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lv(e),X(e)},children:e},l))})}),(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Public Model Name",name:"model_name",tooltip:"Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name.",className:"mb-0",children:(0,a.jsx)(j.Z,{})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,a.jsxs)(k.Z.Item,{label:"LiteLLM Model Name(s)",tooltip:"Actual model name used for making litellm.completion() / litellm.embedding() call.",className:"mb-0",children:[(0,a.jsx)(k.Z.Item,{name:"model",rules:[{required:!0,message:"Required"}],noStyle:!0,children:"Azure"===Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"===Y||"Ollama"===Y?(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())}):P.length>0?(0,a.jsxs)(em.Z,{children:[(0,a.jsx)(eu.Z,{value:"custom",children:"Custom Model Name (Enter below)"}),P.map((e,l)=>(0,a.jsx)(eu.Z,{value:e,children:e},l))]}):(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())})}),(0,a.jsx)(k.Z.Item,{noStyle:!0,shouldUpdate:(e,l)=>e.model!==l.model,children:e=>{let{getFieldValue:l}=e;return(l("model")||[]).includes("custom")&&(0,a.jsx)(k.Z.Item,{name:"custom_model_name",rules:[{required:!0,message:"Please enter a custom model name"}],className:"mt-2",children:(0,a.jsx)(j.Z,{placeholder:"Enter custom model name"})})}})]}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Actual model name used for making"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," ","models with the same 'public name'"]})})]}),void 0!==l&&l.fields.length>0&&(0,a.jsx)(eN,{fields:l.fields,selectedProvider:l.name}),"Amazon Bedrock"!=Y&&"Vertex AI (Anthropic, Gemini, etc.)"!=Y&&"Ollama"!=Y&&(void 0===l||0==l.fields.length)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,a.jsx)(j.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==Y&&(0,a.jsx)(k.Z.Item,{label:"Organization ID",name:"organization",children:(0,a.jsx)(j.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,a.jsx)(j.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,a.jsx)(ev.Z,{name:"file",accept:".json",beforeUpload:e=>{if("application/json"===e.type){let l=new FileReader;l.onload=e=>{if(e.target){let l=e.target.result;f.setFieldsValue({vertex_credentials:l})}},l.readAsText(e)}return!1},onChange(e){"uploading"!==e.file.status&&console.log(e.file,e.fileList),"done"===e.file.status?S.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&S.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,a.jsx)(C.ZP,{icon:(0,a.jsx)(eb.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"==Y)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,a.jsx)(j.Z,{placeholder:"https://..."})}),"Azure"==Y&&(0,a.jsx)(k.Z.Item,{label:"API Version",name:"api_version",tooltip:"By default litellm will use the latest version. If you want to use a different version, you can specify it here",children:(0,a.jsx)(j.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==Y&&(0,a.jsxs)("div",{children:[(0,a.jsx)(k.Z.Item,{label:"Base Model",name:"base_model",className:"mb-0",children:(0,a.jsx)(j.Z,{placeholder:"azure/gpt-3.5-turbo"})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-2",children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from"," ",(0,a.jsx)(eC,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})})]})]}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Access Key ID",name:"aws_access_key_id",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Secret Access Key",name:"aws_secret_access_key",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Region Name",name:"aws_region_name",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),(0,a.jsx)(k.Z.Item,{label:"LiteLLM Params",name:"litellm_extra_params",tooltip:"Optional litellm params used for making a litellm.completion() call.",className:"mb-0",children:(0,a.jsx)(ef.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,a.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Model"})}),(0,a.jsx)(ep.Z,{title:"Get help on our github",children:(0,a.jsx)(es.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,a.jsx)(p.Z,{onClick:lk,children:"Run `/health`"}),$&&(0,a.jsx)("pre",{children:JSON.stringify($,null,2)})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:4,className:"mt-2 mb-2",children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:e8,className:"mr-2",onValueChange:e=>{e3(e),lS(eL,e.from,e.to)}})]}),(0,a.jsxs)(ej.Z,{className:"ml-2",children:[(0,a.jsx)(_.Z,{children:"Select Model Group"}),(0,a.jsx)(H.Z,{defaultValue:eL||eR[0],value:eL||eR[0],children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>lS(e,e8.from,e8.to),children:e},l))})]}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(eZ.Z,{trigger:"click",content:lw,overlayStyle:{width:"20vw"},children:(0,a.jsx)(p.Z,{icon:e_.Z,size:"md",variant:"secondary",className:"mt-4 ml-2",style:{border:"none"},onClick:()=>lr(!0)})})})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"mr-2 max-h-[400px] min-h-[400px]",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Avg. Latency per Token"}),(0,a.jsx)(er.Z,{value:"2",children:"✨ Time to first token"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("p",{className:"text-gray-500 italic",children:" (seconds/token)"}),(0,a.jsx)(_.Z,{className:"text-gray-500 italic mt-1 mb-1",children:"average Latency for successfull requests divided by the total tokens"}),eB&&eK&&(0,a.jsx)(eh.Z,{title:"Model Latency",className:"h-72",data:eB,showLegend:!1,index:"date",categories:eK,connectNulls:!0,customTooltip:lN})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(ew,{modelMetrics:eH,modelMetricsCategories:eJ,customTooltip:lN,premiumUser:h})})]})]})})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"ml-2 max-h-[400px] min-h-[400px] overflow-y-auto",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Deployment"}),(0,a.jsx)(K.Z,{children:"Success Responses"}),(0,a.jsxs)(K.Z,{children:["Slow Responses ",(0,a.jsx)("p",{children:"Success Responses taking 600+s"})]})]})}),(0,a.jsx)(z.Z,{children:e4.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.api_base}),(0,a.jsx)(B.Z,{children:e.total_count}),(0,a.jsx)(B.Z,{children:e.slow_count})]},l))})]})})})]}),(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Exceptions for ",eL]}),(0,a.jsx)(ex.Z,{className:"h-60",data:eX,index:"model",categories:eQ,stack:!0,yAxisWidth:30})]})}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Up Rate Limit Errors (429) for ",eL]}),(0,a.jsxs)(x.Z,{numItems:1,children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",ll.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:ll.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]}),(0,a.jsx)(ej.Z,{})]})]}),h?(0,a.jsx)(a.Fragment,{children:lt.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base?e.api_base:"Unknown API Base"}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors (429) ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]},l))}):(0,a.jsx)(a.Fragment,{children:lt&<.length>0&<.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Rate Limit Errors by Deployment"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see exceptions for all deployments"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]})]},l))})]})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsx)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||eR[0],value:eL||eR[0],onValueChange:e=>eU(e),children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))})]}),(0,a.jsxs)(y.Z,{children:["Retry Policy for ",eL]}),(0,a.jsx)(_.Z,{className:"mb-6",children:"How many retries should be attempted based on the Exception"}),eT&&(0,a.jsx)("table",{children:(0,a.jsx)("tbody",{children:Object.entries(eT).map((e,l)=>{var s;let[t,n]=e,r=null==e6?void 0:null===(s=e6[eL])||void 0===s?void 0:s[n];return null==r&&(r=e9),(0,a.jsxs)("tr",{className:"flex justify-between items-center mt-2",children:[(0,a.jsx)("td",{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)("td",{children:(0,a.jsx)(A.Z,{className:"ml-5",value:r,min:0,step:1,onChange:e=>{e7(l=>{var s;let t=null!==(s=null==l?void 0:l[eL])&&void 0!==s?s:{};return{...null!=l?l:{},[eL]:{...t,[n]:e}}})}})})]},l)})})}),(0,a.jsx)(p.Z,{className:"mt-6 mr-8",onClick:lg,children:"Save"})]})]})]})})},eR=e=>{let{isInvitationLinkModalVisible:l,setIsInvitationLinkModalVisible:s,baseUrl:t,invitationLinkData:n}=e,{Title:r,Paragraph:i}=es.default,o=()=>(null==n?void 0:n.has_user_setup_sso)?"".concat(t,"/ui"):"".concat(t,"/ui?invitation_id=").concat(null==n?void 0:n.id);return(0,a.jsxs)(w.Z,{title:"Invitation Link",visible:l,width:800,footer:null,onOk:()=>{s(!1)},onCancel:()=>{s(!1)},children:[(0,a.jsx)(i,{children:"Copy and send the generated link to onboard this user to the proxy."}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"User ID"}),(0,a.jsx)(_.Z,{children:null==n?void 0:n.user_id})]}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{children:"Invitation Link"}),(0,a.jsx)(_.Z,{children:(0,a.jsx)(_.Z,{children:o()})})]}),(0,a.jsxs)("div",{className:"flex justify-end mt-5",children:[(0,a.jsx)("div",{}),(0,a.jsx)(b.CopyToClipboard,{text:o(),onCopy:()=>S.ZP.success("Copied!"),children:(0,a.jsx)(p.Z,{variant:"primary",children:"Copy invitation link"})})]})]})};let{Option:eF}=v.default;var eM=e=>{let{userID:l,accessToken:s,teams:t,possibleUIRoles:n}=e,[o,d]=(0,r.useState)(null),[c]=k.Z.useForm(),[m,h]=(0,r.useState)(!1),[x,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),[y,b]=(0,r.useState)(!1),[N,A]=(0,r.useState)(null),P=(0,i.useRouter)();console.log=function(){};let[T,E]=(0,r.useState)("");(0,r.useEffect)(()=>{(async()=>{try{let e=await (0,u.So)(s,l,"any"),t=[];for(let l=0;l{if(P){let{protocol:e,host:l}=window.location;E("".concat(e,"/").concat(l))}},[P]);let O=async e=>{try{var t;S.ZP.info("Making API Call"),h(!0),console.log("formValues in create user:",e);let n=await (0,u.Ov)(s,null,e);console.log("user create Response:",n),g(n.key);let a=(null===(t=n.data)||void 0===t?void 0:t.user_id)||n.user_id;if(null==o?void 0:o.SSO_ENABLED){let e={id:crypto.randomUUID(),user_id:a,is_accepted:!1,accepted_at:null,expires_at:new Date(Date.now()+6048e5),created_at:new Date,created_by:l,updated_at:new Date,updated_by:l,has_user_setup_sso:!0};A(e),b(!0)}else(0,u.XO)(s,a).then(e=>{e.has_user_setup_sso=!1,A(e),b(!0)});S.ZP.success("API user Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-0",onClick:()=>h(!0),children:"+ Invite User"}),(0,a.jsxs)(w.Z,{title:"Invite User",visible:m,width:800,footer:null,onOk:()=>{h(!1),c.resetFields()},onCancel:()=>{h(!1),g(null),c.resetFields()},children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Create a User who can own keys"}),(0,a.jsxs)(k.Z,{form:c,onFinish:O,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(k.Z.Item,{label:"User Email",name:"user_email",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:n&&Object.entries(n).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,a.jsx)(v.default,{placeholder:"Select Team ID",style:{width:"100%"},children:t?t.map(e=>(0,a.jsx)(eF,{value:e.team_id,children:e.team_alias},e.team_id)):(0,a.jsx)(eF,{value:null,children:"Default Team"},"default")})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create User"})})]})]}),x&&(0,a.jsx)(eR,{isInvitationLinkModalVisible:y,setIsInvitationLinkModalVisible:b,baseUrl:T,invitationLinkData:N})]})},eD=e=>{let{visible:l,possibleUIRoles:s,onCancel:t,user:n,onSubmit:i}=e,[o,d]=(0,r.useState)(n),[c]=k.Z.useForm();(0,r.useEffect)(()=>{c.resetFields()},[n]);let m=async()=>{c.resetFields(),t()},u=async e=>{i(e),c.resetFields(),t()};return n?(0,a.jsx)(w.Z,{visible:l,onCancel:m,footer:null,title:"Edit User "+n.user_id,width:1e3,children:(0,a.jsx)(k.Z,{form:c,onFinish:u,initialValues:n,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"User Email",tooltip:"Email of the User",name:"user_email",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"user_id",name:"user_id",hidden:!0,children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:s&&Object.entries(s).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Spend (USD)",name:"spend",tooltip:"(float) - Spend of all LLM calls completed by this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"User Budget (USD)",name:"max_budget",tooltip:"(float) - Maximum budget of this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})}):null};console.log=function(){};var eL=e=>{let{accessToken:l,token:s,keys:t,userRole:n,userID:i,teams:o,setKeys:d}=e,[c,m]=(0,r.useState)(null),[h,p]=(0,r.useState)(null),[j,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)(1),[_,y]=r.useState(null),[b,v]=(0,r.useState)(null),[k,w]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[A,C]=(0,r.useState)({}),P=async()=>{I(null),w(!1)},T=async e=>{if(console.log("inside handleEditSubmit:",e),l&&s&&n&&i){try{await (0,u.pf)(l,e,null),S.ZP.success("User ".concat(e.user_id," updated successfully"))}catch(e){console.error("There was an error updating the user",e)}h&&p(h.map(l=>l.user_id===e.user_id?e:l)),I(null),w(!1)}};return((0,r.useEffect)(()=>{if(!l||!s||!n||!i)return;let e=async()=>{try{let e=await (0,u.Br)(l,null,n,!0,Z,25);m(e),console.log("user data response:",e),p(e.users||[]);let s=await (0,u.lg)(l);C(s)}catch(e){console.error("There was an error fetching the model data",e)}};l&&s&&n&&i&&e()},[l,s,n,i,Z]),h&&l&&s&&n&&i)?(0,a.jsx)("div",{style:{width:"100%"},children:(0,a.jsxs)(x.Z,{className:"gap-2 p-2 h-[90vh] w-full mt-8",children:[(0,a.jsx)(eM,{userID:i,accessToken:l,teams:o,possibleUIRoles:A}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[90vh] mb-4",children:[(0,a.jsx)("div",{className:"mb-4 mt-1"}),(0,a.jsx)(ei.Z,{children:(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(V.Z,{className:"mt-5",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"User ID"}),(0,a.jsx)(K.Z,{children:"User Email"}),(0,a.jsx)(K.Z,{children:"Role"}),(0,a.jsx)(K.Z,{children:"User Spend ($ USD)"}),(0,a.jsx)(K.Z,{children:"User Max Budget ($ USD)"}),(0,a.jsx)(K.Z,{children:"API Keys"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:h.map(e=>{var l,s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_id||"-"}),(0,a.jsx)(B.Z,{children:e.user_email||"-"}),(0,a.jsx)(B.Z,{children:(null==A?void 0:null===(l=A[null==e?void 0:e.user_role])||void 0===l?void 0:l.ui_label)||"-"}),(0,a.jsx)(B.Z,{children:e.spend?null===(s=e.spend)||void 0===s?void 0:s.toFixed(2):"-"}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(x.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,a.jsxs)(D.Z,{size:"xs",color:"indigo",children:[e.key_aliases.filter(e=>null!==e).length,"\xa0Keys"]}):(0,a.jsx)(D.Z,{size:"xs",color:"gray",children:"No Keys"})})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,onClick:()=>{I(e),w(!0)},children:"View Keys"})})]},e.user_id)})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"flex-1"}),(0,a.jsx)("div",{className:"flex-1 flex justify-between items-center"})]})})]})}),(0,a.jsx)(eD,{visible:k,possibleUIRoles:A,onCancel:P,user:N,onSubmit:T})]}),function(){if(!h)return null;let e=(null==c?void 0:c.total_pages)||0,l=e=>{p([]),f(e)};return(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsxs)("div",{children:["Showing Page ",Z," of ",e]}),(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===Z,onClick:()=>l(Z-1),children:"← Prev"}),(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:Z===e,onClick:()=>l(Z+1),children:"Next →"})]})]})}()]})}):(0,a.jsx)("div",{children:"Loading..."})};console.log=function(){};var eU=e=>{let{teams:l,searchParams:s,accessToken:t,setTeams:n,userID:i,userRole:o}=e;(0,r.useEffect)(()=>{console.log("inside useeffect - ".concat(l)),null===l&&t&&(async()=>{let e=await (0,u.It)(t);console.log("givenTeams: ".concat(e)),n(e)})()},[l]);let[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:g}=es.default,[Z,f]=(0,r.useState)(""),[y,b]=(0,r.useState)(!1),[N,P]=(0,r.useState)(l?l[0]:null),[T,E]=(0,r.useState)(!1),[O,F]=(0,r.useState)(!1),[J,Y]=(0,r.useState)([]),[X,$]=(0,r.useState)(!1),[Q,ee]=(0,r.useState)(null),[el,et]=(0,r.useState)({}),en=e=>{P(e),b(!0)},ea=async e=>{let s=e.team_id;if(console.log("handleEditSubmit:",e),null==t)return;let a=await (0,u.Gh)(t,e);l&&n(l.map(e=>e.team_id===s?a.data:e)),S.ZP.success("Team updated successfully"),b(!1),P(null)},er=async e=>{ee(e),$(!0)},ei=async()=>{if(null!=Q&&null!=l&&null!=t){try{await (0,u.rs)(t,Q);let e=l.filter(e=>e.team_id!==Q);n(e)}catch(e){console.error("Error deleting the team:",e)}$(!1),ee(null)}};(0,r.useEffect)(()=>{let e=async()=>{try{if(null===i||null===o||null===t||null===l)return;let e={},s=await (0,u.It)(t);for(let l=0;l{try{if(null===i||null===o)return;if(null!==t){let e=(await (0,u.So)(t,i,o)).data.map(e=>e.id);console.log("available_model_names:",e),Y(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,i,o,l]);let eo=async e=>{try{if(null!=t){var s;let a=null==e?void 0:e.team_alias;if((null!==(s=null==l?void 0:l.map(e=>e.team_alias))&&void 0!==s?s:[]).includes(a))throw Error("Team alias ".concat(a," already exists, please pick another alias"));S.ZP.info("Creating Team");let r=await (0,u.hT)(t,e);null!==l?n([...l,r]):n([r]),console.log("response for team create call: ".concat(r)),S.ZP.success("Team created"),E(!1)}}catch(e){console.error("Error creating the team:",e),S.ZP.error("Error creating the team: "+e,20)}},ed=async e=>{try{if(null!=t&&null!=l){S.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},a=await (0,u.cu)(t,N.team_id,s);console.log("response for team create call: ".concat(a.data));let r=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(a.data.team_id)),e.team_id===a.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...l];e[r]=a.data,n(e),P(a.data)}F(!1)}}catch(e){console.error("Error creating the team:",e)}};return(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"All Teams"}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Team Name"}),(0,a.jsx)(K.Z,{children:"Team ID"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"TPM / RPM Limits"}),(0,a.jsx)(K.Z,{children:"Info"})]})}),(0,a.jsx)(z.Z,{children:l&&l.length>0?l.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",fontSize:"0.75em"},children:(0,a.jsx)(ep.Z,{title:e.team_id,children:e.team_id})}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!==e.max_budget&&void 0!==e.max_budget?e.max_budget:"No limit"}),(0,a.jsx)(B.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].keys&&el[e.team_id].keys.length," ","Keys"]}),(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].team_info&&el[e.team_id].team_info.members_with_roles&&el[e.team_id].team_info.members_with_roles.length," ","Members"]})]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>en(e)}),(0,a.jsx)(U.Z,{onClick:()=>er(e.team_id),icon:M.Z,size:"sm"})]})]},e.team_id)):null})]}),X&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:ei,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{$(!1),ee(null)},children:"Cancel"})]})]})]})})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>E(!0),children:"+ Create New Team"}),(0,a.jsx)(w.Z,{title:"Create Team",visible:T,width:800,footer:null,onOk:()=>{E(!1),d.resetFields()},onCancel:()=>{E(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:eo,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"Team Members"}),(0,a.jsx)(g,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{P(e)},children:e.team_alias},l))}):(0,a.jsxs)(g,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:N?N.members_with_roles.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsx)(B.Z,{children:e.role})]},l)):null})]})}),N&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,team:t,onSubmit:n}=e,[r]=k.Z.useForm();return(0,a.jsx)(w.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n({...e,team_id:t.team_id}),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:ea,initialValues:t,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J&&J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:y,onCancel:()=>{b(!1),P(null)},team:N,onSubmit:ea})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-5",onClick:()=>F(!0),children:"+ Add member"}),(0,a.jsx)(w.Z,{title:"Add member",visible:O,width:800,footer:null,onOk:()=>{F(!1),c.resetFields()},onCancel:()=>{F(!1),c.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:ed,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,a.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,a.jsx)(k.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eV=e=>{let l,{searchParams:s,accessToken:t,showSSOBanner:n,premiumUser:o}=e,[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:j}=es.default,[g,Z]=(0,r.useState)(""),[f,y]=(0,r.useState)(null),[b,v]=(0,r.useState)(null),[N,A]=(0,r.useState)(!1),[P,T]=(0,r.useState)(!1),[E,O]=(0,r.useState)(!1),[F,M]=(0,r.useState)(!1),[D,J]=(0,r.useState)(!1),[Y,X]=(0,r.useState)(!1),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(!1),[et,en]=(0,r.useState)(!1),[ea,er]=(0,r.useState)([]),[ei,eo]=(0,r.useState)(null),ed=(0,i.useRouter)(),[ec,em]=(0,r.useState)(null);console.log=function(){};let[eu,eh]=(0,r.useState)(""),ex="All IP Addresses Allowed";try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let ep=async()=>{try{if(!0!==o){S.ZP.error("This feature is only available for premium users. Please upgrade your account.");return}if(t){let e=await (0,u.PT)(t);er(e&&e.length>0?e:[ex])}else er([ex])}catch(e){console.error("Error fetching allowed IPs:",e),S.ZP.error("Failed to fetch allowed IPs ".concat(e)),er([ex])}finally{!0===o&&Q(!0)}},ej=async e=>{try{if(t){await (0,u.eH)(t,e.ip);let l=await (0,u.PT)(t);er(l),S.ZP.success("IP address added successfully")}}catch(e){console.error("Error adding IP:",e),S.ZP.error("Failed to add IP address ".concat(e))}finally{el(!1)}},eg=async e=>{eo(e),en(!0)},eZ=async()=>{if(ei&&t)try{await (0,u.$I)(t,ei);let e=await (0,u.PT)(t);er(e.length>0?e:[ex]),S.ZP.success("IP address deleted successfully")}catch(e){console.error("Error deleting IP:",e),S.ZP.error("Failed to delete IP address ".concat(e))}finally{en(!1),eo(null)}},ef=()=>{X(!1)},e_=["proxy_admin","proxy_admin_viewer"];(0,r.useEffect)(()=>{if(ed){let{protocol:e,host:l}=window.location;eh("".concat(e,"//").concat(l))}},[ed]),(0,r.useEffect)(()=>{(async()=>{if(null!=t){let e=[],l=await (0,u.Xd)(t,"proxy_admin_viewer");console.log("proxy admin viewer response: ",l);let s=l.users;console.log("proxy viewers response: ".concat(s)),s.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(s));let n=(await (0,u.Xd)(t,"proxy_admin")).users;n.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(n)),console.log("combinedList: ".concat(e)),y(e),em(await (0,u.lg)(t))}})()},[t]);let ey=()=>{M(!1),c.resetFields(),d.resetFields()},eb=()=>{M(!1),c.resetFields(),d.resetFields()},ev=e=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-8 mt-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},className:"mt-4",children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]}),eS=(e,l,s)=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:l,children:e_.map((e,l)=>(0,a.jsx)(G.Z,{value:e,children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:s,valuePropName:"user_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s,disabled:!0})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update role"})})]}),ew=async e=>{try{if(null!=t&&null!=f){S.ZP.info("Making API Call");let l=await (0,u.pf)(t,e,null);console.log("response for team create call: ".concat(l));let s=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(l.user_id)),e.user_id===l.user_id));console.log("foundIndex: ".concat(s)),-1==s&&(console.log("updates admin with new user"),f.push(l),y(f)),S.ZP.success("Refresh tab to see updated user role"),M(!1)}}catch(e){console.error("Error creating the key:",e)}},eN=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call");let s=await (0,u.pf)(t,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(s));let n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)});let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(s.user_id)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),T(!1)}}catch(e){console.error("Error creating the key:",e)}},eI=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call"),e.user_email,e.user_id;let s=await (0,u.pf)(t,e,"proxy_admin"),n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)}),console.log("response for team create call: ".concat(s));let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(n)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),O(!1)}}catch(e){console.error("Error creating the key:",e)}},eA=async e=>{if(null==t)return;let l={environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}};(0,u.K_)(t,l)};return console.log("admins: ".concat(null==f?void 0:f.length)),(0,a.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,a.jsx)(m,{level:4,children:"Admin Access "}),(0,a.jsxs)(j,{children:[n&&(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc."," ",(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:f?f.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsxs)(B.Z,{children:[" ",(null==ec?void 0:null===(s=ec[null==e?void 0:e.user_role])||void 0===s?void 0:s.ui_label)||"-"]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>M(!0)}),(0,a.jsx)(w.Z,{title:"Update role",visible:F,width:800,footer:null,onOk:ey,onCancel:eb,children:eS(ew,e.user_role,e.user_id)})]})]},l)}):null})]})})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("div",{className:"flex justify-start",children:[(0,a.jsx)(p.Z,{className:"mr-4 mb-5",onClick:()=>O(!0),children:"+ Add admin"}),(0,a.jsx)(w.Z,{title:"Add admin",visible:E,width:800,footer:null,onOk:()=>{O(!1),c.resetFields(),d.resetFields()},onCancel:()=>{O(!1),A(!1),c.resetFields(),d.resetFields()},children:ev(eI)}),(0,a.jsx)(eR,{isInvitationLinkModalVisible:N,setIsInvitationLinkModalVisible:A,baseUrl:eu,invitationLinkData:b}),(0,a.jsx)(p.Z,{className:"mb-5",onClick:()=>T(!0),children:"+ Add viewer"}),(0,a.jsx)(w.Z,{title:"Add viewer",visible:P,width:800,footer:null,onOk:()=>{T(!1),c.resetFields(),d.resetFields()},onCancel:()=>{T(!1),c.resetFields(),d.resetFields()},children:ev(eN)})]})})]}),(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(m,{level:4,children:" ✨ Security Settings"}),(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem",marginTop:"1rem"},children:[(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:()=>!0===o?J(!0):S.ZP.error("Only premium users can add SSO"),children:"Add SSO"})}),(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:ep,children:"Allowed IPs"})})]})]}),(0,a.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,a.jsx)(w.Z,{title:"Add SSO",visible:D,width:800,footer:null,onOk:()=>{J(!1),d.resetFields()},onCancel:()=>{J(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:e=>{eI(e),eA(e),J(!1),X(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,a.jsx)(I.Z.Password,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,a.jsx)(I.Z.Password,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,a.jsxs)(w.Z,{title:"SSO Setup Instructions",visible:Y,width:800,footer:null,onOk:ef,onCancel:()=>{X(!1)},children:[(0,a.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{onClick:ef,children:"Done"})})]}),(0,a.jsx)(w.Z,{title:"Manage Allowed IP Addresses",width:800,visible:$,onCancel:()=>Q(!1),footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>el(!0),children:"Add IP Address"},"add"),(0,a.jsx)(p.Z,{onClick:()=>Q(!1),children:"Close"},"close")],children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"IP Address"}),(0,a.jsx)(K.Z,{className:"text-right",children:"Action"})]})}),(0,a.jsx)(z.Z,{children:ea.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e}),(0,a.jsx)(B.Z,{className:"text-right",children:e!==ex&&(0,a.jsx)(p.Z,{onClick:()=>eg(e),color:"red",size:"xs",children:"Delete"})})]},l))})]})}),(0,a.jsx)(w.Z,{title:"Add Allowed IP Address",visible:ee,onCancel:()=>el(!1),footer:null,children:(0,a.jsxs)(k.Z,{onFinish:ej,children:[(0,a.jsx)(k.Z.Item,{name:"ip",rules:[{required:!0,message:"Please enter an IP address"}],children:(0,a.jsx)(I.Z,{placeholder:"Enter IP address"})}),(0,a.jsx)(k.Z.Item,{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add IP Address"})})]})}),(0,a.jsx)(w.Z,{title:"Confirm Delete",visible:et,onCancel:()=>en(!1),onOk:eZ,footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>eZ(),children:"Yes"},"delete"),(0,a.jsx)(p.Z,{onClick:()=>en(!1),children:"Close"},"close")],children:(0,a.jsxs)("p",{children:["Are you sure you want to delete the IP address: ",ei,"?"]})})]}),(0,a.jsxs)(ek.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access"," ",(0,a.jsxs)("a",{href:l,target:"_blank",children:[(0,a.jsx)("b",{children:l})," "]})]})]})]})},ez=s(42556),eB=s(90252),eq=e=>{let{alertingSettings:l,handleInputChange:s,handleResetField:t,handleSubmit:n,premiumUser:r}=e,[i]=k.Z.useForm();return(0,a.jsxs)(k.Z,{form:i,onFinish:()=>{console.log("INSIDE ONFINISH");let e=i.getFieldsValue(),l=Object.entries(e).every(e=>{let[l,s]=e;return"boolean"!=typeof s&&(""===s||null==s)});console.log("formData: ".concat(JSON.stringify(e),", isEmpty: ").concat(l)),l?console.log("Some form fields are empty."):n(e)},labelAlign:"left",children:[l.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{align:"center",children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?r?(0,a.jsx)(k.Z.Item,{name:e.field_name,children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l)}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>s(e.field_name,l)}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}):(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,a.jsx)(k.Z.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l),className:"p-0"}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>{s(e.field_name,l),i.setFieldsValue({[e.field_name]:l})}}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>t(e.field_name,l),children:"Reset"})})]},l)),(0,a.jsx)("div",{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update Settings"})})]})},eK=e=>{let{accessToken:l,premiumUser:s}=e,[t,n]=(0,r.useState)([]);return(0,r.useEffect)(()=>{l&&(0,u.RQ)(l).then(e=>{n(e)})},[l]),(0,a.jsx)(eq,{alertingSettings:t,handleInputChange:(e,l)=>{let s=t.map(s=>s.field_name===e?{...s,field_value:l}:s);console.log("updatedSettings: ".concat(JSON.stringify(s))),n(s)},handleResetField:(e,s)=>{if(l)try{let l=t.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:l.field_default_value}:l);n(l)}catch(e){console.log("ERROR OCCURRED!")}},handleSubmit:e=>{if(!l||(console.log("formValues: ".concat(e)),null==e||void 0==e))return;let s={};t.forEach(e=>{s[e.field_name]=e.field_value});let n={...e,...s};console.log("mergedFormValues: ".concat(JSON.stringify(n)));let{slack_alerting:a,...r}=n;console.log("slack_alerting: ".concat(a,", alertingArgs: ").concat(JSON.stringify(r)));try{(0,u.jA)(l,"alerting_args",r),"boolean"==typeof a&&(!0==a?(0,u.jA)(l,"alerting",["slack"]):(0,u.jA)(l,"alerting",[])),S.ZP.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:s})},eW=s(84406);let{Title:eH,Paragraph:eG}=es.default;console.log=function(){};var eJ=e=>{let{accessToken:l,userRole:s,userID:t,premiumUser:n}=e,[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)([]),[m,h]=(0,r.useState)(!1),[g]=k.Z.useForm(),[Z,f]=(0,r.useState)(null),[y,b]=(0,r.useState)([]),[N,I]=(0,r.useState)(""),[A,P]=(0,r.useState)({}),[T,E]=(0,r.useState)([]),[O,F]=(0,r.useState)(!1),[M,D]=(0,r.useState)([]),[H,J]=(0,r.useState)(null),[Y,X]=(0,r.useState)([]),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(null),es=e=>{T.includes(e)?E(T.filter(l=>l!==e)):E([...T,e])},et={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,r.useEffect)(()=>{l&&s&&t&&(0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e),o(e.callbacks),D(e.available_callbacks);let l=e.alerts;if(console.log("alerts_data",l),l&&l.length>0){let e=l[0];console.log("_alert_info",e);let s=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",s),E(e.active_alerts),I(s),P(e.alerts_to_webhook)}c(l)})},[l,s,t]);let en=e=>T&&T.includes(e),ea=()=>{if(!l)return;let e={};d.filter(e=>"email"===e.name).forEach(l=>{var s;Object.entries(null!==(s=l.variables)&&void 0!==s?s:{}).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));n&&n.value&&(e[s]=null==n?void 0:n.value)})}),console.log("updatedVariables",e);try{(0,u.K_)(l,{general_settings:{alerting:["email"]},environment_variables:e})}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Email settings updated successfully")},em=async e=>{if(!l)return;let s={};Object.entries(e).forEach(e=>{let[l,t]=e;"callback"!==l&&(s[l]=t)});try{await (0,u.K_)(l,{environment_variables:s}),S.ZP.success("Callback added successfully"),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eu=async e=>{if(!l)return;let s=null==e?void 0:e.callback,t={};Object.entries(e).forEach(e=>{let[l,s]=e;"callback"!==l&&(t[l]=s)});try{await (0,u.K_)(l,{environment_variables:t,litellm_settings:{success_callback:[s]}}),S.ZP.success("Callback ".concat(s," added successfully")),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eh=e=>{console.log("inside handleSelectedCallbackChange",e),f(e.litellm_callback_name),console.log("all callbacks",M),e&&e.litellm_callback_params?(X(e.litellm_callback_params),console.log("selectedCallbackParams",Y)):X([])};return l?(console.log("callbacks: ".concat(i)),(0,a.jsxs)("div",{className:"w-full mx-4",children:[(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Logging Callbacks"}),(0,a.jsx)(er.Z,{value:"2",children:"Alerting Types"}),(0,a.jsx)(er.Z,{value:"3",children:"Alerting Settings"}),(0,a.jsx)(er.Z,{value:"4",children:"Email Alerts"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Active Logging Callbacks"}),(0,a.jsx)(x.Z,{numItems:2,children:(0,a.jsx)(L.Z,{className:"max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(K.Z,{children:"Callback Name"})})}),(0,a.jsx)(z.Z,{children:i.map((e,s)=>(0,a.jsxs)(W.Z,{className:"flex justify-between",children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.name})}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"flex justify-between",children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>{el(e),Q(!0)}}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,e.name),className:"ml-2",variant:"secondary",children:"Test Callback"})]})})]},s))})]})})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>F(!0),children:"Add Callback"})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(_.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,a.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{children:"Slack Webhook URL"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(et).map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:"region_outage_alerts"==s?n?(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)}):(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:s,type:"password",defaultValue:A&&A[s]?A[s]:N})})]},l)})})]}),(0,a.jsx)(p.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(et).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));console.log("key",s),console.log("webhookInput",n);let a=(null==n?void 0:n.value)||"";console.log("newWebhookValue",a),e[s]=a}),console.log("updatedAlertToWebhooks",e);let s={general_settings:{alert_to_webhook_url:e,alert_types:T}};console.log("payload",s);try{(0,u.K_)(l,s)}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"slack"),className:"mx-2",children:"Test Alerts"})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eK,{accessToken:l,premiumUser:n})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Email Settings"}),(0,a.jsxs)(_.Z,{children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: email alerts"})," ",(0,a.jsx)("br",{})]}),(0,a.jsx)("div",{className:"flex w-full",children:d.filter(e=>"email"===e.name).map((e,l)=>{var s;return(0,a.jsx)(B.Z,{children:(0,a.jsx)("ul",{children:(0,a.jsx)(x.Z,{numItems:2,children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).map(e=>{let[l,s]=e;return(0,a.jsxs)("li",{className:"mx-2 my-2",children:[!0!=n&&("EMAIL_LOGO_URL"===l||"EMAIL_SUPPORT_CONTACT"===l)?(0,a.jsxs)("div",{children:[(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,a.jsxs)(_.Z,{className:"mt-2",children:[" ","✨ ",l]})}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-2",children:l}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",style:{width:"400px"}})]}),(0,a.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===l&&(0,a.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},l)})})})},l)})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>ea(),children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"email"),className:"mx-2",children:"Test Email Alerts"})]})})]})]})}),(0,a.jsxs)(w.Z,{title:"Add Logging Callback",visible:O,width:800,onCancel:()=>F(!1),footer:null,children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: Logging"}),(0,a.jsx)(k.Z,{form:g,onFinish:eu,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(eW.Z,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,a.jsx)(v.default,{onChange:e=>{let l=M[e];l&&(console.log(l.ui_callback_name),eh(l))},children:M&&Object.values(M).map(e=>(0,a.jsx)(G.Z,{value:e.litellm_callback_name,children:e.ui_callback_name},e.litellm_callback_name))})}),Y&&Y.map(e=>(0,a.jsx)(eW.Z,{label:e,name:e,rules:[{required:!0,message:"Please enter the value for "+e}],children:(0,a.jsx)(j.Z,{type:"password"})},e)),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]}),(0,a.jsx)(w.Z,{visible:$,width:800,title:"Edit ".concat(null==ee?void 0:ee.name," Settings"),onCancel:()=>Q(!1),footer:null,children:(0,a.jsxs)(k.Z,{form:g,onFinish:em,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:ee&&ee.variables&&Object.entries(ee.variables).map(e=>{let[l,s]=e;return(0,a.jsx)(eW.Z,{label:l,name:l,children:(0,a.jsx)(j.Z,{type:"password",defaultValue:s})},l)})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:eY}=v.default;var eX=e=>{let{models:l,accessToken:s,routerSettings:t,setRouterSettings:n}=e,[i]=k.Z.useForm(),[o,d]=(0,r.useState)(!1),[c,m]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>d(!0),children:"+ Add Fallbacks"}),(0,a.jsx)(w.Z,{title:"Add Fallbacks",visible:o,width:800,footer:null,onOk:()=>{d(!1),i.resetFields()},onCancel:()=>{d(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:e=>{console.log(e);let{model_name:l,models:a}=e,r=[...t.fallbacks||[],{[l]:a}],o={...t,fallbacks:r};console.log(o);try{(0,u.K_)(s,{router_settings:o}),n(o)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully"),d(!1),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,a.jsx)(H.Z,{defaultValue:c,children:l&&l.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsx)(em.Z,{value:l,children:l&&l.filter(e=>e!=c).map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},e$=s(12968);async function eQ(e,l){console.log=function(){},console.log("isLocal:",!1);let s=window.location.origin,t=new e$.ZP.OpenAI({apiKey:l,baseURL:s,dangerouslyAllowBrowser:!0});try{let l=await t.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.ZP.success((0,a.jsxs)("span",{children:["Test model=",(0,a.jsx)("strong",{children:e}),", received model=",(0,a.jsx)("strong",{children:l.model}),". See"," ",(0,a.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}let e0={ttl:3600,lowest_latency_buffer:0},e1=e=>{let{selectedStrategy:l,strategyArgs:s,paramExplanation:t}=e;return(0,a.jsxs)(g.Z,{children:[(0,a.jsx)(f.Z,{className:"text-sm font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong",children:"Routing Strategy Specific Args"}),(0,a.jsx)(Z.Z,{children:"latency-based-routing"==l?(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(s).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:t[l]})]}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]})}):(0,a.jsx)(_.Z,{children:"No specific settings"})})]})};var e2=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)({}),[d,c]=(0,r.useState)({}),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(!1),[b]=k.Z.useForm(),[v,w]=(0,r.useState)(null),[N,I]=(0,r.useState)(null),[C,P]=(0,r.useState)(null),T={routing_strategy_args:"(dict) Arguments to pass to the routing strategy",routing_strategy:"(string) Routing strategy to use",allowed_fails:"(int) Number of times a deployment can fail before being added to cooldown",cooldown_time:"(int) time in seconds to cooldown a deployment after failure",num_retries:"(int) Number of retries for failed requests. Defaults to 0.",timeout:"(float) Timeout for requests. Defaults to None.",retry_after:"(int) Minimum time to wait before retrying a failed request",ttl:"(int) Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"(float) Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};(0,r.useEffect)(()=>{l&&s&&t&&((0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e);let l=e.router_settings;"model_group_retry_policy"in l&&delete l.model_group_retry_policy,o(l)}),(0,u.YU)(l).then(e=>{g(e)}))},[l,s,t]);let E=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(i.fallbacks)),i.fallbacks.map(l=>(e in l&&delete l[e],l));try{await (0,u.K_)(l,{router_settings:i}),o({...i}),I(i.routing_strategy),S.ZP.success("Router settings updated successfully")}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}}},O=(e,l)=>{g(m.map(s=>s.field_name===e?{...s,field_value:l}:s))},R=(e,s)=>{if(!l)return;let t=m[s].field_value;if(null!=t&&void 0!=t)try{(0,u.jA)(l,e,t);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:!0}:l);g(s)}catch(e){}},F=(e,s)=>{if(l)try{(0,u.ao)(l,e);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:null}:l);g(s)}catch(e){}},J=e=>{if(!l)return;console.log("router_settings",e);let s=Object.fromEntries(Object.entries(e).map(e=>{let[l,s]=e;if("routing_strategy_args"!==l&&"routing_strategy"!==l){var t;return[l,(null===(t=document.querySelector('input[name="'.concat(l,'"]')))||void 0===t?void 0:t.value)||s]}if("routing_strategy"==l)return[l,N];if("routing_strategy_args"==l&&"latency-based-routing"==N){let e={},l=document.querySelector('input[name="lowest_latency_buffer"]'),s=document.querySelector('input[name="ttl"]');return(null==l?void 0:l.value)&&(e.lowest_latency_buffer=Number(l.value)),(null==s?void 0:s.value)&&(e.ttl=Number(s.value)),console.log("setRoutingStrategyArgs: ".concat(e)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));console.log("updatedVariables",s);try{(0,u.K_)(l,{router_settings:s})}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully")};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Loadbalancing"}),(0,a.jsx)(er.Z,{value:"2",children:"Fallbacks"}),(0,a.jsx)(er.Z,{value:"3",children:"General"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,a.jsx)(y.Z,{children:"Router Settings"}),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(i).filter(e=>{let[l,s]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l&&"routing_strategy_args"!=l}).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:T[l]})]}),(0,a.jsx)(B.Z,{children:"routing_strategy"==l?(0,a.jsxs)(H.Z,{defaultValue:s,className:"w-full max-w-md",onValueChange:I,children:[(0,a.jsx)(G.Z,{value:"usage-based-routing",children:"usage-based-routing"}),(0,a.jsx)(G.Z,{value:"latency-based-routing",children:"latency-based-routing"}),(0,a.jsx)(G.Z,{value:"simple-shuffle",children:"simple-shuffle"})]}):(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]}),(0,a.jsx)(e1,{selectedStrategy:N,strategyArgs:i&&i.routing_strategy_args&&Object.keys(i.routing_strategy_args).length>0?i.routing_strategy_args:e0,paramExplanation:T})]}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>J(i),children:"Save Changes"})})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model Name"}),(0,a.jsx)(K.Z,{children:"Fallbacks"})]})}),(0,a.jsx)(z.Z,{children:i.fallbacks&&i.fallbacks.map((e,s)=>Object.entries(e).map(e=>{let[t,n]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:t}),(0,a.jsx)(B.Z,{children:Array.isArray(n)?n.join(", "):n}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>eQ(t,l),children:"Test Fallback"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>E(t)})})]},s.toString()+t)}))})]}),(0,a.jsx)(eX,{models:(null==n?void 0:n.data)?n.data.map(e=>e.model_name):[],accessToken:l,routerSettings:i,setRouterSettings:o})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"}),(0,a.jsx)(K.Z,{children:"Status"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:m.filter(e=>"TypedDictionary"!==e.field_type).map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),(0,a.jsx)(B.Z,{children:"Integer"==e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>O(e.field_name,l)}):null}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(p.Z,{onClick:()=>R(e.field_name,l),children:"Update"}),(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>F(e.field_name,l),children:"Reset"})]})]},l))})]})})})]})]})}):null},e4=s(98786),e5=s(74325),e8=e=>{let{value:l={},onChange:s}=e,[t,n]=(0,r.useState)(Object.entries(l)),i=e=>{let l=t.filter((l,s)=>s!==e);n(l),null==s||s(Object.fromEntries(l))},o=(e,l,a)=>{let r=[...t];r[e]=[l,a],n(r),null==s||s(Object.fromEntries(r))};return(0,a.jsxs)("div",{children:[t.map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(c.Z,{style:{display:"flex",marginBottom:8},align:"start",children:[(0,a.jsx)(j.Z,{placeholder:"Header Name",value:s,onChange:e=>o(l,e.target.value,t)}),(0,a.jsx)(j.Z,{placeholder:"Header Value",value:t,onChange:e=>o(l,s,e.target.value)}),(0,a.jsx)(e4.Z,{onClick:()=>i(l)})]},l)}),(0,a.jsx)(C.ZP,{type:"dashed",onClick:()=>{n([...t,["",""]])},icon:(0,a.jsx)(e5.Z,{}),children:"Add Header"})]})};let{Option:e3}=v.default;var e6=e=>{let{accessToken:l,setPassThroughItems:s,passThroughItems:t}=e,[n]=k.Z.useForm(),[i,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>o(!0),children:"+ Add Pass-Through Endpoint"}),(0,a.jsx)(w.Z,{title:"Add Pass-Through Endpoint",visible:i,width:800,footer:null,onOk:()=>{o(!1),n.resetFields()},onCancel:()=>{o(!1),n.resetFields()},children:(0,a.jsxs)(k.Z,{form:n,onFinish:e=>{console.log(e);let a=[...t,{headers:e.headers,path:e.path,target:e.target}];try{(0,u.Vt)(l,e),s(a)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("Pass through endpoint successfully added"),o(!1),n.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Path",name:"path",rules:[{required:!0,message:"The route to be added to the LiteLLM Proxy Server."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Target",name:"target",rules:[{required:!0,message:"The URL to which requests for this path should be forwarded."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Headers",name:"headers",rules:[{required:!0,message:"Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint"}],help:"required",children:(0,a.jsx)(e8,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Pass-Through Endpoint"})})]})})]})},e7=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&(0,u.mp)(l).then(e=>{o(e.endpoints)})},[l,s,t]);let d=(e,s)=>{if(l)try{(0,u.EG)(l,e);let s=i.filter(l=>l.path!==e);o(s),S.ZP.success("Endpoint deleted successfully.")}catch(e){}};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Path"}),(0,a.jsx)(K.Z,{children:"Target"}),(0,a.jsx)(K.Z,{children:"Headers"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:i.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.path})}),(0,a.jsx)(B.Z,{children:e.target}),(0,a.jsx)(B.Z,{children:JSON.stringify(e.headers)}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>d(e.path,l),children:"Reset"})})]},l))})]}),(0,a.jsx)(e6,{accessToken:l,setPassThroughItems:o,passThroughItems:i})]})})}):null},e9=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n}=e,[r]=k.Z.useForm(),i=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),r.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Create Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),r.resetFields()},onCancel:()=>{t(!1),r.resetFields()},children:(0,a.jsxs)(k.Z,{form:r,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Budget"})})]})})},le=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n,existingBudget:r}=e,[i]=k.Z.useForm(),o=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),i.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Edit Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),i.resetFields()},onCancel:()=>{t(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:r,children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Budget"})})]})})},ll=e=>{let{accessToken:l}=e,[s,t]=(0,r.useState)(!1),[n,i]=(0,r.useState)(!1),[o,d]=(0,r.useState)(null),[c,m]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&(0,u.O3)(l).then(e=>{m(e)})},[l]);let h=async(e,s)=>{null!=l&&(d(c[s]),i(!0))},x=async(e,s)=>{if(null==l)return;S.ZP.info("Request made"),await (0,u.NV)(l,e);let t=[...c];t.splice(s,1),m(t),S.ZP.success("Budget Deleted.")};return(0,a.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,a.jsx)(p.Z,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>t(!0),children:"+ Create Budget"}),(0,a.jsx)(e9,{accessToken:l,isModalVisible:s,setIsModalVisible:t,setBudgetList:m}),o&&(0,a.jsx)(le,{accessToken:l,isModalVisible:n,setIsModalVisible:i,setBudgetList:m,existingBudget:o}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"Create a budget to assign to customers."}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Budget ID"}),(0,a.jsx)(K.Z,{children:"Max Budget"}),(0,a.jsx)(K.Z,{children:"TPM"}),(0,a.jsx)(K.Z,{children:"RPM"})]})}),(0,a.jsx)(z.Z,{children:c.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.budget_id}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"n/a"}),(0,a.jsx)(B.Z,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,a.jsx)(B.Z,{children:e.rpm_limit?e.rpm_limit:"n/a"}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>h(e.budget_id,l)}),(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>x(e.budget_id,l)})]},l))})]})]}),(0,a.jsxs)("div",{className:"mt-5",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"How to use budget id"}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"Assign Budget to Customer"}),(0,a.jsx)(er.Z,{children:"Test it (Curl)"}),(0,a.jsx)(er.Z,{children:"Test it (OpenAI SDK)"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:"\ncurl -X POST --location '/end_user/new' \n-H 'Authorization: Bearer ' \n-H 'Content-Type: application/json' \n-d '{\"user_id\": \"my-customer-id', \"budget_id\": \"\"}' # \uD83D\uDC48 KEY CHANGE\n\n "})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:'\ncurl -X POST --location \'/chat/completions\' \n-H \'Authorization: Bearer \' \n-H \'Content-Type: application/json\' \n-d \'{\n "model": "gpt-3.5-turbo\', \n "messages":[{"role": "user", "content": "Hey, how\'s it going?"}],\n "user": "my-customer-id"\n}\' # \uD83D\uDC48 KEY CHANGE\n\n '})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'from openai import OpenAI\nclient = OpenAI(\n base_url="",\n api_key=""\n)\n\ncompletion = client.chat.completions.create(\n model="gpt-3.5-turbo",\n messages=[\n {"role": "system", "content": "You are a helpful assistant."},\n {"role": "user", "content": "Hello!"}\n ],\n user="my-customer-id"\n)\n\nprint(completion.choices[0].message)'})})]})]})]})]})},ls=s(41134),lt=e=>{let{proxySettings:l}=e,s="";return l&&l.PROXY_BASE_URL&&void 0!==l.PROXY_BASE_URL&&(s=l.PROXY_BASE_URL),(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,a.jsx)(_.Z,{className:"mt-2 mb-2",children:"LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below "}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"OpenAI Python SDK"}),(0,a.jsx)(er.Z,{children:"LlamaIndex"}),(0,a.jsx)(er.Z,{children:"Langchain Py"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="'.concat(s,'" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="'.concat(s,'", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="').concat(s,'",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="'.concat(s,'",\n model = "gpt-3.5-turbo",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})})};async function ln(e,l,s,t){console.log=function(){},console.log("isLocal:",!1);let n=window.location.origin,a=new e$.ZP.OpenAI({apiKey:t,baseURL:n,dangerouslyAllowBrowser:!0});try{for await(let t of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(t),t.choices[0].delta.content&&l(t.choices[0].delta.content)}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var la=e=>{let{accessToken:l,token:s,userRole:t,userID:n}=e,[i,o]=(0,r.useState)(""),[d,c]=(0,r.useState)(""),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(void 0),[y,b]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&n&&(async()=>{try{let e=await (0,u.So)(l,n,t);if(console.log("model_info:",e),(null==e?void 0:e.data.length)>0){let l=e.data.map(e=>({value:e.id,label:e.id}));if(console.log(l),l.length>0){let e=Array.from(new Set(l));console.log("Unique models:",e),e.sort((e,l)=>e.label.localeCompare(l.label)),console.log("Model info:",y),b(e)}f(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,n,t]);let k=(e,l)=>{g(s=>{let t=s[s.length-1];return t&&t.role===e?[...s.slice(0,s.length-1),{role:e,content:t.content+l}]:[...s,{role:e,content:l}]})},S=async()=>{if(""!==d.trim()&&i&&s&&t&&n){g(e=>[...e,{role:"user",content:d}]);try{Z&&await ln(d,e=>k("assistant",e),Z,i)}catch(e){console.error("Error fetching model response",e),k("assistant","Error fetching model response")}c("")}};if(t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,a.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsx)(eo.Z,{children:(0,a.jsx)(er.Z,{children:"Chat"})}),(0,a.jsx)(ec.Z,{children:(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("div",{className:"sm:max-w-2xl",children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"API Key"}),(0,a.jsx)(j.Z,{placeholder:"Type API Key here",type:"password",onValueChange:o,value:i})]}),(0,a.jsxs)(h.Z,{className:"mx-2",children:[(0,a.jsx)(_.Z,{children:"Select Model:"}),(0,a.jsx)(v.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),f(e)},options:y,style:{width:"200px"}})]})]})}),(0,a.jsxs)(V.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{})})}),(0,a.jsx)(z.Z,{children:m.map((e,l)=>(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,a.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(j.Z,{type:"text",value:d,onChange:e=>c(e.target.value),onKeyDown:e=>{"Enter"===e.key&&S()},placeholder:"Type your message..."}),(0,a.jsx)(p.Z,{onClick:S,className:"ml-2",children:"Send"})]})})]})})]})})})})},lr=s(33509),li=s(95781);let{Sider:lo}=lr.default,ld=["Admin","Admin Viewer","Internal User","Internal Viewer"];var lc=e=>{let{setPage:l,userRole:s,defaultSelectedKey:t}=e;return"Admin Viewer"==s?(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,a.jsx)(lo,{width:120,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["4"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9")]})})}):(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,a.jsx)(lo,{width:145,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["1"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("api-keys"),children:(0,a.jsx)(_.Z,{children:"Virtual Keys"})},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("llm-playground"),children:(0,a.jsx)(_.Z,{children:"Test Key"})},"3"),"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("models"),children:(0,a.jsx)(_.Z,{children:"Models"})},"2"):null,ld.includes(s)?(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:(0,a.jsx)(_.Z,{children:"Usage"})},"4"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("users"),children:(0,a.jsx)(_.Z,{children:"Internal Users"})},"5"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("settings"),children:(0,a.jsx)(_.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("budgets"),children:(0,a.jsx)(_.Z,{children:"Budgets"})},"10"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("general-settings"),children:(0,a.jsx)(_.Z,{children:"Router Settings"})},"11"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("pass-through-settings"),children:(0,a.jsx)(_.Z,{children:"Pass-Through"})},"12"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("admin-panel"),children:(0,a.jsx)(_.Z,{children:"Admin Settings"})},"13"):null,(0,a.jsx)(li.Z.Item,{onClick:()=>l("api_ref"),children:(0,a.jsx)(_.Z,{children:"API Reference"})},"14"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("model-hub"),children:(0,a.jsx)(_.Z,{children:"Model Hub"})},"16")]})})})},lm=s(67989),lu=s(52703);console.log("process.env.NODE_ENV","production"),console.log=function(){};let lh=e=>null!==e&&("Admin"===e||"Admin Viewer"===e);var lx=e=>{let{accessToken:l,token:s,userRole:t,userID:n,keys:i,premiumUser:o}=e,d=new Date,[c,m]=(0,r.useState)([]),[j,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)([]),[b,v]=(0,r.useState)([]),[k,S]=(0,r.useState)([]),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)([]),[C,P]=(0,r.useState)([]),[T,E]=(0,r.useState)([]),[O,R]=(0,r.useState)([]),[F,M]=(0,r.useState)({}),[D,U]=(0,r.useState)([]),[J,Y]=(0,r.useState)(""),[$,Q]=(0,r.useState)(["all-tags"]),[ee,el]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),es=new Date(d.getFullYear(),d.getMonth(),1),et=new Date(d.getFullYear(),d.getMonth()+1,0),ep=e_(es),ej=e_(et);function eg(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}console.log("keys in usage",i),console.log("premium user in usage",o),(0,r.useEffect)(()=>{ef(ee.from,ee.to)},[ee,$]);let eZ=async(e,s,t)=>{if(!e||!s||!l)return;s.setHours(23,59,59,999),e.setHours(0,0,0,0),console.log("uiSelectedKey",t);let n=await (0,u.b1)(l,t,e.toISOString(),s.toISOString());console.log("End user data updated successfully",n),v(n)},ef=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),N((await (0,u.J$)(l,e.toISOString(),s.toISOString(),0===$.length?void 0:$)).spend_per_tag),console.log("Tag spend data updated successfully"))};function e_(e){let l=e.getFullYear(),s=e.getMonth()+1,t=e.getDate();return"".concat(l,"-").concat(s<10?"0"+s:s,"-").concat(t<10?"0"+t:t)}console.log("Start date is ".concat(ep)),console.log("End date is ".concat(ej));let ey=async(e,l,s)=>{try{let s=await e();l(s)}catch(e){console.error(s,e)}},eb=()=>ey(()=>l?(0,u.FC)(l):Promise.reject("No access token"),m,"Error fetching overall spend"),ev=()=>ey(()=>l&&s?(0,u.OU)(l,s,ep,ej):Promise.reject("No access token or token"),R,"Error fetching provider spend"),ek=async()=>{l&&await ey(async()=>(await (0,u.tN)(l)).map(e=>({key:(e.key_alias||e.key_name||e.api_key).substring(0,10),spend:e.total_spend})),g,"Error fetching top keys")},eS=async()=>{l&&await ey(async()=>(await (0,u.Au)(l)).map(e=>({key:e.model,spend:e.total_spend})),f,"Error fetching top models")},ew=async()=>{l&&await ey(async()=>{let e=await (0,u.mR)(l);return S(e.daily_spend),P(e.teams),e.total_spend_per_team.map(e=>({name:e.team_id||"",value:(e.total_spend||0).toFixed(2)}))},E,"Error fetching team spend")},eN=()=>{l&&ey(async()=>(await (0,u.X)(l)).tag_names,A,"Error fetching tag names")},eI=()=>{l&&ey(()=>{var e,s;return(0,u.J$)(l,null===(e=ee.from)||void 0===e?void 0:e.toISOString(),null===(s=ee.to)||void 0===s?void 0:s.toISOString(),void 0)},e=>N(e.spend_per_tag),"Error fetching top tags")},eA=()=>{l&&ey(()=>(0,u.b1)(l,null,void 0,void 0),v,"Error fetching top end users")},eC=()=>{l&&ey(()=>(0,u.wd)(l,ep,ej),M,"Error fetching global activity")},eP=()=>{l&&ey(()=>(0,u.xA)(l,ep,ej),U,"Error fetching global activity per model")};return(0,r.useEffect)(()=>{l&&s&&t&&n&&(eb(),ev(),ek(),eS(),eC(),eP(),lh(t)&&(ew(),eN(),eI(),eA()))},[l,s,t,n,ep,ej]),(0,a.jsx)("div",{style:{width:"100%"},className:"p-8",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{className:"mt-2",children:[(0,a.jsx)(er.Z,{children:"All Up"}),lh(t)?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(er.Z,{children:"Team Based Usage"}),(0,a.jsx)(er.Z,{children:"Customer Usage"}),(0,a.jsx)(er.Z,{children:"Tag Based Usage"})]}):(0,a.jsx)(a.Fragment,{children:(0,a.jsx)("div",{})})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"solid",className:"mt-1",children:[(0,a.jsx)(er.Z,{children:"Cost"}),(0,a.jsx)(er.Z,{children:"Activity"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[100vh] w-full",children:[(0,a.jsx)(X,{userID:n,userRole:t,accessToken:l,userSpend:null,selectedTeam:null,userMaxBudget:null}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Monthly Spend"}),(0,a.jsx)(ex.Z,{data:c,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top API Keys"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:j,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top Models"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:Z,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"✨ Spend by Provider"}),o?(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(lu.Z,{className:"mt-4 h-40",variant:"pie",data:O,index:"provider",category:"spend"})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Provider"}),(0,a.jsx)(K.Z,{children:"Spend"})]})}),(0,a.jsx)(z.Z,{children:O.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.provider}),(0,a.jsx)(B.Z,{children:1e-5>parseFloat(e.spend.toFixed(2))?"less than 0.00":e.spend.toFixed(2)})]},e.provider))})]})})]})}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})]})]})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"All Up"}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(F.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["api_requests"],onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(F.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]}),o?(0,a.jsx)(a.Fragment,{children:D.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["total_tokens"],valueFormatter:eg,onValueChange:e=>console.log(e)})]})]})]},l))}):(0,a.jsx)(a.Fragment,{children:D&&D.length>0&&D.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Activity by Model"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see analytics for all models"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],valueFormatter:eg,categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]})]},l))})]})})]})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(h.Z,{numColSpan:2,children:[(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"Total Spend Per Team"}),(0,a.jsx)(lm.Z,{data:T})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Daily Spend Per Team"}),(0,a.jsx)(ex.Z,{className:"h-72",data:k,showLegend:!0,index:"date",categories:C,yAxisWidth:80,stack:!0})]})]}),(0,a.jsx)(h.Z,{numColSpan:2})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:["Customers of your LLM API calls. Tracked when a `user` param is passed in your LLM calls ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/users",target:"_blank",children:"docs here"})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:ee,onValueChange:e=>{el(e),eZ(e.from,e.to,null)}})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Key"}),(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{eZ(ee.from,ee.to,null)},children:"All Keys"},"all-keys"),null==i?void 0:i.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{eZ(ee.from,ee.to,e.token)},children:e.key_alias},l):null)]})]})]}),(0,a.jsx)(L.Z,{className:"mt-4",children:(0,a.jsxs)(V.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Customer"}),(0,a.jsx)(K.Z,{children:"Spend"}),(0,a.jsx)(K.Z,{children:"Total Events"})]})}),(0,a.jsx)(z.Z,{children:null==b?void 0:b.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.end_user}),(0,a.jsx)(B.Z,{children:null===(s=e.total_spend)||void 0===s?void 0:s.toFixed(4)}),(0,a.jsx)(B.Z,{children:e.total_count})]},l)})})]})})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(ea.Z,{className:"mb-4",enableSelect:!0,value:ee,onValueChange:e=>{el(e),ef(e.from,e.to)}})}),(0,a.jsx)(h.Z,{children:o?(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsx)(eu.Z,{value:String(e),children:e},e))]})}):(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsxs)(G.Z,{value:String(e),disabled:!0,children:["✨ ",e," (Enterprise only Feature)"]},e))]})})})]}),(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Spend Per Tag"}),(0,a.jsxs)(_.Z,{children:["Get Started Tracking cost per tag ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/cost_tracking",target:"_blank",children:"here"})]}),(0,a.jsx)(ex.Z,{className:"h-72",data:w,index:"name",categories:["spend"],colors:["blue"]})]})}),(0,a.jsx)(h.Z,{numColSpan:2})]})]})]})]})})};let lp=e=>{if(e)return e.toISOString().split("T")[0]};function lj(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}var lg=e=>{let{accessToken:l,token:s,userRole:t,userID:n,premiumUser:i}=e,[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[p,j]=(0,r.useState)([]),[g,Z]=(0,r.useState)([]),[f,_]=(0,r.useState)("0"),[y,b]=(0,r.useState)("0"),[v,k]=(0,r.useState)("0"),[S,w]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date});(0,r.useEffect)(()=>{l&&S&&(async()=>{Z(await (0,u.zg)(l,lp(S.from),lp(S.to)))})()},[l]);let N=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.api_key)&&void 0!==l?l:""}))),I=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.model)&&void 0!==l?l:""})));Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.call_type)&&void 0!==l?l:""})));let A=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),Z(await (0,u.zg)(l,lp(e),lp(s))))};return(0,r.useEffect)(()=>{console.log("DATA IN CACHE DASHBOARD",g);let e=g;c.length>0&&(e=e.filter(e=>c.includes(e.api_key))),p.length>0&&(e=e.filter(e=>p.includes(e.model))),console.log("before processed data in cache dashboard",e);let l=0,s=0,t=0,n=e.reduce((e,n)=>{console.log("Processing item:",n),n.call_type||(console.log("Item has no call_type:",n),n.call_type="Unknown"),l+=(n.total_rows||0)-(n.cache_hit_true_rows||0),s+=n.cache_hit_true_rows||0,t+=n.cached_completion_tokens||0;let a=e.find(e=>e.name===n.call_type);return a?(a["LLM API requests"]+=(n.total_rows||0)-(n.cache_hit_true_rows||0),a["Cache hit"]+=n.cache_hit_true_rows||0,a["Cached Completion Tokens"]+=n.cached_completion_tokens||0,a["Generated Completion Tokens"]+=n.generated_completion_tokens||0):e.push({name:n.call_type,"LLM API requests":(n.total_rows||0)-(n.cache_hit_true_rows||0),"Cache hit":n.cache_hit_true_rows||0,"Cached Completion Tokens":n.cached_completion_tokens||0,"Generated Completion Tokens":n.generated_completion_tokens||0}),e},[]);_(lj(s)),b(lj(t));let a=s+l;a>0?k((s/a*100).toFixed(2)):k("0"),d(n),console.log("PROCESSED DATA IN CACHE DASHBOARD",n)},[c,p,S,g]),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(x.Z,{numItems:3,className:"gap-4 mt-4",children:[(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select API Keys",value:c,onValueChange:m,children:N.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select Models",value:p,onValueChange:j,children:I.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(ea.Z,{enableSelect:!0,value:S,onValueChange:e=>{w(e),A(e.from,e.to)},selectPlaceholder:"Select date range"})})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-4",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hit Ratio"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsxs)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:[v,"%"]})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hits"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:f})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cached Tokens"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:y})})]})]}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cache Hits vs API Requests"}),(0,a.jsx)(ex.Z,{title:"Cache Hits vs API Requests",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["LLM API requests","Cache hit"],colors:["sky","teal"],yAxisWidth:48}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cached Completion Tokens vs Generated Completion Tokens"}),(0,a.jsx)(ex.Z,{className:"mt-6",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["Generated Completion Tokens","Cached Completion Tokens"],colors:["sky","teal"],yAxisWidth:48})]})},lZ=()=>{let{Title:e,Paragraph:l}=es.default,[s,t]=(0,r.useState)(""),[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)(null),[h,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(null),[g,Z]=(0,r.useState)({PROXY_BASE_URL:"",PROXY_LOGOUT_URL:""}),[f,_]=(0,r.useState)(!0),y=(0,i.useSearchParams)(),[b,v]=(0,r.useState)({data:[]}),k=y.get("userID"),S=y.get("invitation_id"),w=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),[N,I]=(0,r.useState)("api-keys"),[A,C]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(w){let e=(0,el.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),C(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"internal_user":return"Internal User";case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),t(l),"Admin Viewer"==l&&I("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?_("username_password"==e.login_method):console.log("User Email is not set ".concat(e)),e.premium_user&&o(e.premium_user),e.auth_header_name&&(0,u.K8)(e.auth_header_name)}}},[w]),(0,a.jsx)(r.Suspense,{fallback:(0,a.jsx)("div",{children:"Loading..."}),children:S?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):(0,a.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,a.jsx)(m,{userID:k,userRole:s,userEmail:d,premiumUser:n,setProxySettings:Z,proxySettings:g}),(0,a.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,a.jsx)("div",{className:"mt-8",children:(0,a.jsx)(lc,{setPage:I,userRole:s,defaultSelectedKey:null})}),"api-keys"==N?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):"models"==N?(0,a.jsx)(eO,{userID:k,userRole:s,token:w,keys:p,accessToken:A,modelData:b,setModelData:v,premiumUser:n}):"llm-playground"==N?(0,a.jsx)(la,{userID:k,userRole:s,token:w,accessToken:A}):"users"==N?(0,a.jsx)(eL,{userID:k,userRole:s,token:w,keys:p,teams:h,accessToken:A,setKeys:j}):"teams"==N?(0,a.jsx)(eU,{teams:h,setTeams:x,searchParams:y,accessToken:A,userID:k,userRole:s}):"admin-panel"==N?(0,a.jsx)(eV,{setTeams:x,searchParams:y,accessToken:A,showSSOBanner:f,premiumUser:n}):"api_ref"==N?(0,a.jsx)(lt,{proxySettings:g}):"settings"==N?(0,a.jsx)(eJ,{userID:k,userRole:s,accessToken:A,premiumUser:n}):"budgets"==N?(0,a.jsx)(ll,{accessToken:A}):"general-settings"==N?(0,a.jsx)(e2,{userID:k,userRole:s,accessToken:A,modelData:b}):"model-hub"==N?(0,a.jsx)(ls.Z,{accessToken:A,publicPage:!1,premiumUser:n}):"caching"==N?(0,a.jsx)(lg,{userID:k,userRole:s,token:w,accessToken:A,premiumUser:n}):"pass-through-settings"==N?(0,a.jsx)(e7,{userID:k,userRole:s,accessToken:A,modelData:b}):(0,a.jsx)(lx,{userID:k,userRole:s,token:w,accessToken:A,keys:p,premiumUser:n})]})]})})}},41134:function(e,l,s){"use strict";s.d(l,{Z:function(){return y}});var t=s(57437),n=s(2265),a=s(47907),r=s(777),i=s(2179),o=s(13810),d=s(92836),c=s(26734),m=s(41608),u=s(32126),h=s(23682),x=s(71801),p=s(42440),j=s(84174),g=s(50459),Z=s(6180),f=s(99129),_=s(67951),y=e=>{var l;let{accessToken:s,publicPage:y,premiumUser:b}=e,[v,k]=(0,n.useState)(!1),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(!1),[A,C]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),E=(0,a.useRouter)();(0,n.useEffect)(()=>{s&&(async()=>{try{let e=await (0,r.kn)(s);console.log("ModelHubData:",e),w(e.data),(0,r.E9)(s,"enable_public_model_hub").then(e=>{console.log("data: ".concat(JSON.stringify(e))),!0==e.field_value&&k(!0)}).catch(e=>{})}catch(e){console.error("There was an error fetching the model data",e)}})()},[s,y]);let O=e=>{T(e),I(!0)},R=async()=>{s&&(0,r.jA)(s,"enable_public_model_hub",!0).then(e=>{C(!0)})},F=()=>{I(!1),C(!1),T(null)},M=()=>{I(!1),C(!1),T(null)},D=e=>{navigator.clipboard.writeText(e)};return(0,t.jsxs)("div",{children:[y&&v||!1==y?(0,t.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,t.jsx)("div",{className:"relative w-full"}),(0,t.jsxs)("div",{className:"flex ".concat(y?"justify-between":"items-center"),children:[(0,t.jsx)(p.Z,{className:"ml-8 text-center ",children:"Model Hub"}),!1==y?b?(0,t.jsx)(i.Z,{className:"ml-4",onClick:()=>R(),children:"✨ Make Public"}):(0,t.jsx)(i.Z,{className:"ml-4",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Make Public"})}):(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)("p",{children:"Filter by key:"}),(0,t.jsx)(x.Z,{className:"bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center",children:"/ui/model_hub?key="})]})]}),(0,t.jsx)("div",{className:"grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4 pr-8",children:S&&S.map(e=>(0,t.jsxs)(o.Z,{className:"mt-5 mx-8",children:[(0,t.jsxs)("pre",{className:"flex justify-between",children:[(0,t.jsx)(p.Z,{children:e.model_group}),(0,t.jsx)(Z.Z,{title:e.model_group,children:(0,t.jsx)(j.Z,{onClick:()=>D(e.model_group),style:{cursor:"pointer",marginRight:"10px"}})})]}),(0,t.jsxs)("div",{className:"my-5",children:[(0,t.jsxs)(x.Z,{children:["Mode: ",e.mode]}),(0,t.jsxs)(x.Z,{children:["Supports Function Calling:"," ",(null==e?void 0:e.supports_function_calling)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Supports Vision:"," ",(null==e?void 0:e.supports_vision)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Max Input Tokens:"," ",(null==e?void 0:e.max_input_tokens)?null==e?void 0:e.max_input_tokens:"N/A"]}),(0,t.jsxs)(x.Z,{children:["Max Output Tokens:"," ",(null==e?void 0:e.max_output_tokens)?null==e?void 0:e.max_output_tokens:"N/A"]})]}),(0,t.jsx)("div",{style:{marginTop:"auto",textAlign:"right"},children:(0,t.jsxs)("a",{href:"#",onClick:()=>O(e),style:{color:"#1890ff",fontSize:"smaller"},children:["View more ",(0,t.jsx)(g.Z,{})]})})]},e.model_group))})]}):(0,t.jsxs)(o.Z,{className:"mx-auto max-w-xl mt-10",children:[(0,t.jsx)(x.Z,{className:"text-xl text-center mb-2 text-black",children:"Public Model Hub not enabled."}),(0,t.jsx)("p",{className:"text-base text-center text-slate-800",children:"Ask your proxy admin to enable this on their Admin UI."})]}),(0,t.jsx)(f.Z,{title:"Public Model Hub",width:600,visible:A,footer:null,onOk:F,onCancel:M,children:(0,t.jsxs)("div",{className:"pt-5 pb-5",children:[(0,t.jsxs)("div",{className:"flex justify-between mb-4",children:[(0,t.jsx)(x.Z,{className:"text-base mr-2",children:"Shareable Link:"}),(0,t.jsx)(x.Z,{className:"max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded",children:"/ui/model_hub?key="})]}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(i.Z,{onClick:()=>{E.replace("/model_hub?key=".concat(s))},children:"See Page"})})]})}),(0,t.jsx)(f.Z,{title:P&&P.model_group?P.model_group:"Unknown Model",width:800,visible:N,footer:null,onOk:F,onCancel:M,children:P&&(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-4",children:(0,t.jsx)("strong",{children:"Model Information & Usage"})}),(0,t.jsxs)(c.Z,{children:[(0,t.jsxs)(m.Z,{children:[(0,t.jsx)(d.Z,{children:"OpenAI Python SDK"}),(0,t.jsx)(d.Z,{children:"Supported OpenAI Params"}),(0,t.jsx)(d.Z,{children:"LlamaIndex"}),(0,t.jsx)(d.Z,{children:"Langchain Py"})]}),(0,t.jsxs)(h.Z,{children:[(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="'.concat(P.model_group,'", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:"".concat(null===(l=P.supported_openai_params)||void 0===l?void 0:l.map(e=>"".concat(e,"\n")).join(""))})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="'.concat(P.model_group,'", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:4000",\n model = "'.concat(P.model_group,'",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})]})}}},function(e){e.O(0,[665,936,902,131,684,626,777,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7c218fb97a2a9817.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7c218fb97a2a9817.js deleted file mode 100644 index 5643948a48..0000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-7c218fb97a2a9817.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,s){Promise.resolve().then(s.bind(s,68031))},667:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return f}});var t=s(57437),n=s(2265),a=s(47907),r=s(2179),i=s(18190),o=s(13810),d=s(10384),c=s(46453),m=s(71801),u=s(52273),h=s(42440),x=s(30953),p=s(777),j=s(37963),g=s(60620),Z=s(13565);function f(){let[e]=g.Z.useForm(),l=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));l&&l.split("=")[1]}("token");let s=l.get("invitation_id"),[f,_]=(0,n.useState)(null),[y,b]=(0,n.useState)(""),[v,k]=(0,n.useState)(""),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(""),[A,C]=(0,n.useState)("");return(0,n.useEffect)(()=>{s&&(0,p.W_)(s).then(e=>{let l=e.login_url;console.log("login_url:",l),I(l);let s=e.token,t=(0,j.o)(s);C(s),console.log("decoded:",t),_(t.key),console.log("decoded user email:",t.user_email),k(t.user_email),w(t.user_id)})},[s]),(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,t.jsxs)(o.Z,{children:[(0,t.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,t.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,t.jsx)(m.Z,{children:"Claim your user account to login to Admin UI."}),(0,t.jsx)(i.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,t.jsxs)(c.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,t.jsx)(d.Z,{children:"SSO is under the Enterprise Tirer."}),(0,t.jsx)(d.Z,{children:(0,t.jsx)(r.Z,{variant:"primary",className:"mb-2",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,t.jsxs)(g.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",f,"token:",A,"formValues:",e),f&&A&&(e.user_email=v,S&&s&&(0,p.m_)(f,s,S,e.password).then(e=>{var l;let s="/ui/";s+="?userID="+((null===(l=e.data)||void 0===l?void 0:l.user_id)||e.user_id),document.cookie="token="+A,console.log("redirecting to:",s),window.location.href=s}))},children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.Z.Item,{label:"Email Address",name:"user_email",children:(0,t.jsx)(u.Z,{type:"email",disabled:!0,value:v,defaultValue:v,className:"max-w-md"})}),(0,t.jsx)(g.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,t.jsx)(u.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,t.jsx)("div",{className:"mt-10",children:(0,t.jsx)(Z.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}},68031:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return lZ}});var t,n,a=s(57437),r=s(2265),i=s(47907),o=s(8792),d=s(40491),c=s(65270),m=e=>{let{userID:l,userRole:s,userEmail:t,premiumUser:n,setProxySettings:r,proxySettings:i}=e;console.log("User ID:",l),console.log("userEmail:",t),console.log("premiumUser:",n),console.log=function(){};let m="";console.log("PROXY_settings=",i),i&&i.PROXY_LOGOUT_URL&&void 0!==i.PROXY_LOGOUT_URL&&(m=i.PROXY_LOGOUT_URL),console.log("logoutUrl=",m);let u=[{key:"1",label:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{children:["Role: ",s]}),(0,a.jsxs)("p",{children:["ID: ",l]}),(0,a.jsxs)("p",{children:["Premium User: ",String(n)]})]})},{key:"2",label:(0,a.jsx)("p",{onClick:()=>{document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=m},children:"Logout"})}];return(0,a.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,a.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,a.jsx)("div",{className:"flex flex-col items-center",children:(0,a.jsx)(o.default,{href:"/",children:(0,a.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,a.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,a.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[n?null:(0,a.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Get enterprise license"})}),(0,a.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,a.jsx)(d.Z,{menu:{items:u},children:(0,a.jsx)(c.Z,{children:t})})})]})]})},u=s(777),h=s(10384),x=s(46453),p=s(2179),j=s(52273),g=s(26780),Z=s(15595),f=s(6698),_=s(71801),y=s(42440),b=s(42308),v=s(50670),k=s(60620),S=s(80588),w=s(99129),N=s(18559),I=s(44839),A=s(88707),C=s(13565);let{Option:P}=v.default;var T=e=>{let{userID:l,team:s,userRole:t,accessToken:n,data:i,setData:o}=e,[d]=k.Z.useForm(),[c,m]=(0,r.useState)(!1),[T,E]=(0,r.useState)(null),[O,R]=(0,r.useState)(null),[F,M]=(0,r.useState)([]),[D,L]=(0,r.useState)([]),[U,V]=(0,r.useState)("you"),z=()=>{m(!1),d.resetFields()},B=()=>{m(!1),E(null),d.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==n){let e=(await (0,u.So)(n,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),M(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,t]);let q=async e=>{try{var s,t,a;let r=null!==(s=null==e?void 0:e.key_alias)&&void 0!==s?s:"",c=null!==(t=null==e?void 0:e.team_id)&&void 0!==t?t:null;if((null!==(a=null==i?void 0:i.filter(e=>e.team_id===c).map(e=>e.key_alias))&&void 0!==a?a:[]).includes(r))throw Error("Key alias ".concat(r," already exists for team with ID ").concat(c,", please provide another key alias"));if(S.ZP.info("Making API Call"),m(!0),"service_account"===U){let l={};try{l=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}l.service_account_id=e.key_alias,e.metadata=JSON.stringify(l)}let h=await (0,u.wX)(n,l,e);console.log("key create Response:",h),o(e=>e?[...e,h]:[h]),E(h.key),R(h.soft_budget),S.ZP.success("API Key Created"),d.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,r.useEffect)(()=>{L(s&&s.models.length>0?s.models.includes("all-proxy-models")?F:s.models:F)},[s,F]),(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,a.jsx)(w.Z,{title:"Create Key",visible:c,width:800,footer:null,onOk:z,onCancel:B,children:(0,a.jsxs)(k.Z,{form:d,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Owned By",className:"mb-4",children:(0,a.jsxs)(N.ZP.Group,{onChange:e=>V(e.target.value),value:U,children:[(0,a.jsx)(N.ZP,{value:"you",children:"You"}),(0,a.jsx)(N.ZP,{value:"service_account",children:"Service Account"})]})}),(0,a.jsx)(k.Z.Item,{label:"you"===U?"Key Name":"Service Account ID",name:"key_alias",rules:[{required:!0,message:"Please input a ".concat("you"===U?"key name":"service account ID")}],help:"you"===U?"required":"IDs can include letters, numbers, and hyphens",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:s?s.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&d.setFieldsValue({models:["all-team-models"]})},children:[(0,a.jsx)(P,{value:"all-team-models",children:"All Team Models"},"all-team-models"),D.map(e=>(0,a.jsx)(P,{value:e,children:e},e))]})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==s?void 0:s.max_budget)!==null&&(null==s?void 0:s.max_budget)!==void 0?null==s?void 0:s.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.max_budget&&l>s.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(s.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==s?void 0:s.budget_duration)!==null&&(null==s?void 0:s.budget_duration)!==void 0?null==s?void 0:s.budget_duration:"None"),children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==s?void 0:s.tpm_limit)!==null&&(null==s?void 0:s.tpm_limit)!==void 0?null==s?void 0:s.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.tpm_limit&&l>s.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(s.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==s?void 0:s.rpm_limit)!==null&&(null==s?void 0:s.rpm_limit)!==void 0?null==s?void 0:s.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.rpm_limit&&l>s.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(s.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",className:"mt-8",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Key"})})]})}),T&&(0,a.jsx)(w.Z,{visible:c,onOk:z,onCancel:B,footer:null,children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Save your Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:null!=T?(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:T})}),(0,a.jsx)(b.CopyToClipboard,{text:T,onCopy:()=>{S.ZP.success("API Key copied to clipboard")},children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,a.jsx)(_.Z,{children:"Key being created, this might take 30s"})})]})})]})},E=s(66002),O=s(9454),R=s(98941),F=s(63954),M=s(33393),D=s(5),L=s(13810),U=s(61244),V=s(10827),z=s(3851),B=s(2044),q=s(64167),K=s(74480),W=s(7178),H=s(95093),G=s(27166);let{Option:J}=v.default;console.log=function(){};var Y=e=>{let{userID:l,userRole:s,accessToken:t,selectedTeam:n,data:i,setData:o,teams:d,premiumUser:c}=e,[m,g]=(0,r.useState)(!1),[Z,f]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[P,T]=(0,r.useState)(null),[Y,X]=(0,r.useState)(null),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[es,et]=(0,r.useState)(!1),[en,ea]=(0,r.useState)(null),[er,ei]=(0,r.useState)([]),eo=new Set,[ed,ec]=(0,r.useState)(!1),[em,eu]=(0,r.useState)(!1),[eh,ex]=(0,r.useState)(null),[ep,ej]=(0,r.useState)(null),[eg]=k.Z.useForm(),[eZ,ef]=(0,r.useState)(null),[e_,ey]=(0,r.useState)(eo);(0,r.useEffect)(()=>{console.log("in calculateNewExpiryTime for selectedToken",en),(null==ep?void 0:ep.duration)?ef((e=>{if(!e)return null;try{let l;let s=new Date;if(e.endsWith("s"))l=(0,E.Z)(s,{seconds:parseInt(e)});else if(e.endsWith("h"))l=(0,E.Z)(s,{hours:parseInt(e)});else if(e.endsWith("d"))l=(0,E.Z)(s,{days:parseInt(e)});else throw Error("Invalid duration format");return l.toLocaleString("en-US",{year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",hour12:!0})}catch(e){return null}})(ep.duration)):ef(null),console.log("calculateNewExpiryTime:",eZ)},[en,null==ep?void 0:ep.duration]),(0,r.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==t&&null!==s){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),ei(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[t,l,s]);let eb=e=>{ea(e),ec(!0)},ev=async e=>{if(null==t||null==en)return;let l={...en,metadata:e,key:en.token};try{let e=await (0,u.Nc)(t,l);if(console.log("Model limits updated:",e),i){let l=i.map(l=>l.token===en.token?e:l);o(l)}S.ZP.success("Model-specific limits updated successfully")}catch(e){console.error("Error updating model-specific limits:",e),S.ZP.error("Failed to update model-specific limits")}ec(!1),ea(null)};(0,r.useEffect)(()=>{if(d){let e=new Set;d.forEach((l,s)=>{let t=l.team_id;e.add(t)}),ey(e)}},[d]);let ek=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id);let l=null;if(e.budget_duration)switch(e.budget_duration){case"24h":l="daily";break;case"7d":l="weekly";break;case"30d":l="monthly";break;default:l="None"}ea({...e,budget_duration:l}),el(!0)},eS=async e=>{if(null==t)return;let l=e.token;if(e.key=l,e.budget_duration)switch(e.budget_duration){case"daily":e.budget_duration="24h";break;case"weekly":e.budget_duration="7d";break;case"monthly":e.budget_duration="30d"}console.log("handleEditSubmit:",e);let s=await (0,u.Nc)(t,e);console.log("handleEditSubmit: newKeyValues",s),i&&o(i.map(e=>e.token===l?s:e)),S.ZP.success("Key updated successfully"),el(!1),ea(null)},ew=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=i&&(I(e.token),localStorage.removeItem("userData"+l),f(!0))},eN=async()=>{if(null!=N&&null!=i){try{await (0,u.I1)(t,N);let e=i.filter(e=>e.token!==N);o(e)}catch(e){console.error("Error deleting the key:",e)}f(!1),I(null)}},eI=e=>{ea(e),ef(null),eg.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||""}),eu(!0)},eA=(e,l)=>{ej(s=>({...s,[e]:l}))},eC=async()=>{if(!c){S.ZP.error("Regenerate API Key is an Enterprise feature. Please upgrade to use this feature.");return}if(null!=en)try{let e=await eg.validateFields(),l=await (0,u.s0)(t,en.token,e);if(ex(l.key),i){let s=i.map(s=>s.token===(null==en?void 0:en.token)?{...s,key_name:l.key_name,...e}:s);o(s)}eu(!1),eg.resetFields(),S.ZP.success("API Key regenerated successfully")}catch(e){console.error("Error regenerating key:",e),S.ZP.error("Failed to regenerate API Key")}};if(null!=i)return console.log("RERENDER TRIGGERED"),(0,a.jsxs)("div",{children:[(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,a.jsxs)(V.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Key Alias"}),(0,a.jsx)(K.Z,{children:"Secret Key"}),(0,a.jsx)(K.Z,{children:"Expires"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Budget Reset"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"Rate Limits"}),(0,a.jsx)(K.Z,{children:"Rate Limits per model"})]})}),(0,a.jsx)(z.Z,{children:i.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(n){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(e_.has(e.team_id),", selectedTeam id: ").concat(n.team_id)),(null!=n.team_id||null===e.team_id||e_.has(e.team_id))&&e.team_id!=n.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,a.jsx)(_.Z,{children:e.key_alias}):(0,a.jsx)(_.Z,{children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.key_name})}),(0,a.jsx)(B.Z,{children:null!=e.expires?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.expires).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,a.jsx)(B.Z,{children:null!=e.max_budget?(0,a.jsx)(_.Z,{children:e.max_budget}):(0,a.jsx)(_.Z,{children:"Unlimited"})}),(0,a.jsx)(B.Z,{children:null!=e.budget_reset_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.budget_reset_at).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(a.Fragment,{children:n&&n.models&&n.models.length>0?n.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{size:"xs",onClick:()=>eb(e),children:"Edit Limits"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{onClick:()=>{ea(e),et(!0)},icon:O.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:es,onCancel:()=>{et(!1),ea(null)},footer:null,width:800,children:en&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-8",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(en.spend).toFixed(4)}catch(e){return en.spend}})()})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.max_budget?(0,a.jsxs)(a.Fragment,{children:[en.max_budget,en.budget_duration&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("br",{}),"Budget will be reset at ",en.budget_reset_at?new Date(en.budget_reset_at).toLocaleString():"Never"]})]}):(0,a.jsx)(a.Fragment,{children:"Unlimited"})})})]},e.name),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.expires?(0,a.jsx)(a.Fragment,{children:new Date(en.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,a.jsx)(a.Fragment,{children:"Never"})})})]},e.name)]}),(0,a.jsxs)(L.Z,{className:"my-4",children:[(0,a.jsx)(y.Z,{children:"Token Name"}),(0,a.jsx)(_.Z,{className:"my-1",children:en.key_alias?en.key_alias:en.key_name}),(0,a.jsx)(y.Z,{children:"Token ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.token}),(0,a.jsx)(y.Z,{children:"User ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.user_id}),(0,a.jsx)(y.Z,{children:"Metadata"}),(0,a.jsx)(_.Z,{className:"my-1",children:(0,a.jsxs)("pre",{children:[JSON.stringify(en.metadata)," "]})})]}),(0,a.jsx)(p.Z,{className:"mx-auto flex items-center",onClick:()=>{et(!1),ea(null)},children:"Close"})]})}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>ek(e)}),(0,a.jsx)(U.Z,{onClick:()=>eI(e),icon:F.Z,size:"sm"}),(0,a.jsx)(U.Z,{onClick:()=>ew(e),icon:M.Z,size:"sm"})]})]},e.token)})})]}),Z&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:eN,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{f(!1),I(null)},children:"Cancel"})]})]})]})})]}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:i}=e,[o]=k.Z.useForm(),[c,m]=(0,r.useState)(n),[u,h]=(0,r.useState)([]),[x,p]=(0,r.useState)(!1);return(0,a.jsx)(w.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{o.validateFields().then(e=>{o.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:o,onFinish:eS,initialValues:{...t,budget_duration:t.budget_duration},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let s=l.filter(e=>!c.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!c.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(s)),s.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(s,"Team models: ").concat(c.models)):Promise.resolve()}}],children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(J,{value:"all-team-models",children:"All Team Models"},"all-team-models"),c&&c.models?c.models.includes("all-proxy-models")?er.filter(e=>"all-proxy-models"!==e).map(e=>(0,a.jsx)(J,{value:e,children:e},e)):c.models.map(e=>(0,a.jsx)(J,{value:e,children:e},e)):er.map(e=>(0,a.jsx)(J,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==c?void 0:c.max_budget)!==null&&(null==c?void 0:c.max_budget)!==void 0?null==c?void 0:c.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.max_budget&&l>c.max_budget)throw console.log("keyTeam.max_budget: ".concat(c.max_budget)),Error("Budget cannot exceed team max budget: $".concat(c.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Current Reset Budget: ".concat(t.budget_duration,", budget will be reset: ").concat(t.budget_reset_at?new Date(t.budget_reset_at).toLocaleString():"Never"),children:(0,a.jsxs)(v.default,{placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"daily",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"weekly",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"monthly",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"token",name:"token",hidden:!0}),(0,a.jsx)(k.Z.Item,{label:"Team",name:"team_id",className:"mt-8",help:"the team this key belongs to",children:(0,a.jsx)(H.Z,{value:t.team_alias,children:null==d?void 0:d.map((e,l)=>(0,a.jsx)(G.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"TPM Limit (tokens per minute)",name:"tpm_limit",help:"tpm_limit cannot exceed team tpm_limit ".concat((null==c?void 0:c.tpm_limit)!==null&&(null==c?void 0:c.tpm_limit)!==void 0?null==c?void 0:c.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.tpm_limit&&l>c.tpm_limit)throw console.log("keyTeam.tpm_limit: ".concat(c.tpm_limit)),Error("tpm_limit cannot exceed team max tpm_limit: $".concat(c.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"RPM Limit (requests per minute)",name:"rpm_limit",help:"rpm_limit cannot exceed team max tpm_limit: ".concat((null==c?void 0:c.rpm_limit)!==null&&(null==c?void 0:c.rpm_limit)!==void 0?null==c?void 0:c.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.rpm_limit&&l>c.rpm_limit)throw console.log("keyTeam.rpm_limit: ".concat(c.rpm_limit)),Error("rpm_limit cannot exceed team max rpm_limit: $".concat(c.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:ee,onCancel:()=>{el(!1),ea(null)},token:en,onSubmit:eS}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:n,accessToken:i}=e,[o,d]=(0,r.useState)({}),[c,m]=(0,r.useState)([]),[h,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(t.metadata){let e=t.metadata.model_tpm_limit||{},l=t.metadata.model_rpm_limit||{},s={};Object.keys({...e,...l}).forEach(t=>{s[t]={tpm:e[t]||0,rpm:l[t]||0}}),d(s)}(async()=>{try{let e=await (0,u.AZ)(i,"",""),l=Array.from(new Set(e.data.map(e=>e.model_name)));m(l)}catch(e){console.error("Error fetching model data:",e),S.ZP.error("Failed to fetch available models")}})()},[t,i]);let j=(e,l,s)=>{d(t=>({...t,[e]:{...t[e],[l]:s||0}}))},g=e=>{d(l=>{let{[e]:s,...t}=l;return t})};return(0,a.jsxs)(w.Z,{title:"Edit Model-Specific Limits",visible:l,onCancel:s,footer:null,width:800,children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model"}),(0,a.jsx)(K.Z,{children:"TPM Limit"}),(0,a.jsx)(K.Z,{children:"RPM Limit"}),(0,a.jsx)(K.Z,{children:"Actions"})]})}),(0,a.jsxs)(z.Z,{children:[Object.entries(o).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:l}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.tpm,onChange:e=>j(l,"tpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.rpm,onChange:e=>j(l,"rpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>g(l),children:"Remove"})})]},l)}),null!==h&&(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(v.default,{style:{width:200},placeholder:"Select a model",onChange:e=>{o[e]||d(l=>({...l,[e]:{tpm:0,rpm:0}})),x(null)},value:h||void 0,children:c.filter(e=>!o.hasOwnProperty(e)).map(e=>(0,a.jsx)(J,{value:e,children:e},e))})}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>x(null),children:"Cancel"})})]})]})]}),(0,a.jsx)(p.Z,{onClick:()=>{x("")},disabled:null!==h,children:"Add Limit"})]}),(0,a.jsxs)("div",{className:"flex justify-end space-x-4 mt-6",children:[(0,a.jsx)(p.Z,{onClick:s,children:"Cancel"}),(0,a.jsx)(p.Z,{onClick:()=>{n({...t.metadata,model_tpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.tpm]})),model_rpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.rpm]}))})},children:"Save"})]})]})},{visible:ed,onCancel:()=>ec(!1),token:en,onSubmit:ev,accessToken:t}),(0,a.jsx)(w.Z,{title:"Regenerate API Key",visible:em,onCancel:()=>{eu(!1),eg.resetFields()},footer:[(0,a.jsx)(p.Z,{onClick:()=>{eu(!1),eg.resetFields()},className:"mr-2",children:"Cancel"},"cancel"),(0,a.jsx)(p.Z,{onClick:eC,disabled:!c,children:c?"Regenerate":"Upgrade to Regenerate"},"regenerate")],children:c?(0,a.jsxs)(k.Z,{form:eg,layout:"vertical",onValuesChange:(e,l)=>{"duration"in e&&eA("duration",e.duration)},children:[(0,a.jsx)(k.Z.Item,{name:"key_alias",label:"Key Alias",children:(0,a.jsx)(j.Z,{disabled:!0})}),(0,a.jsx)(k.Z.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,a.jsx)(A.Z,{step:.01,precision:2,style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"duration",label:"Expire Key (eg: 30s, 30h, 30d)",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsxs)("div",{className:"mt-2 text-sm text-gray-500",children:["Current expiry: ",(null==en?void 0:en.expires)!=null?new Date(en.expires).toLocaleString():"Never"]}),eZ&&(0,a.jsxs)("div",{className:"mt-2 text-sm text-green-600",children:["New expiry: ",eZ]})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",children:"Get Free Trial"})})]})}),eh&&(0,a.jsx)(w.Z,{visible:!!eh,onCancel:()=>ex(null),footer:[(0,a.jsx)(p.Z,{onClick:()=>ex(null),children:"Close"},"close")],children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Regenerated Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please replace your old key with the new key generated. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"Key Alias:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:(null==en?void 0:en.key_alias)||"No alias set"})}),(0,a.jsx)(_.Z,{className:"mt-3",children:"New API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:eh})}),(0,a.jsx)(b.CopyToClipboard,{text:eh,onCopy:()=>S.ZP.success("API Key copied to clipboard"),children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]})]})})]})};console.log=function(){};var X=e=>{let{userID:l,userRole:s,accessToken:t,userSpend:n,userMaxBudget:i,selectedTeam:o}=e;console.log("userSpend: ".concat(n));let[d,c]=(0,r.useState)(null!==n?n:0),[m,h]=(0,r.useState)(o?o.max_budget:null);(0,r.useEffect)(()=>{if(o){if("Default Team"===o.team_alias)h(i);else{let e=!1;if(o.team_memberships)for(let s of o.team_memberships)s.user_id===l&&"max_budget"in s.litellm_budget_table&&null!==s.litellm_budget_table.max_budget&&(h(s.litellm_budget_table.max_budget),e=!0);e||h(o.max_budget)}}},[o,i]);let[x,p]=(0,r.useState)([]);(0,r.useEffect)(()=>{let e=async()=>{if(!t||!l||!s)return};(async()=>{try{if(null===l||null===s)return;if(null!==t){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),p(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,t,l]),(0,r.useEffect)(()=>{null!==n&&c(n)},[n]);let j=[];o&&o.models&&(j=o.models),j&&j.includes("all-proxy-models")?(console.log("user models:",x),j=x):j&&j.includes("all-team-models")?j=o.models:j&&0===j.length&&(j=x);let g=void 0!==d?d.toFixed(4):null;return console.log("spend in view user spend: ".concat(d)),(0,a.jsx)("div",{className:"flex items-center",children:(0,a.jsxs)("div",{className:"flex justify-between gap-x-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Total Spend"}),(0,a.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",g]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Max Budget"}),(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null!==m?"$".concat(m," limit"):"No limit"})]})]})})};console.log=function(){};var $=e=>{let{userID:l,userRole:s,selectedTeam:t,accessToken:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==n){let e=(await (0,u.So)(n,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),o(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,s]);let d=[];return t&&t.models&&(d=t.models),d&&d.includes("all-proxy-models")&&(console.log("user models:",i),d=i),(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==t?void 0:t.team_alias}),(null==t?void 0:t.team_id)&&(0,a.jsxs)("p",{className:"text-xs text-gray-400 dark:text-gray-400 font-semibold",children:["Team ID: ",null==t?void 0:t.team_id]})]})})},Q=e=>{let l,{teams:s,setSelectedTeam:t,userRole:n,proxySettings:i,setProxySettings:o,userInfo:d,accessToken:c}=e;console.log("userInfo: ".concat(JSON.stringify(d)));let m={models:(null==d?void 0:d.models)||[],team_id:null,team_alias:"Default Team",max_budget:(null==d?void 0:d.max_budget)||null},h=async()=>{null===i&&c&&o(await (0,u.Dj)(c))};(0,r.useEffect)(()=>{h()},[i]);let[x,p]=(0,r.useState)(m);return console.log("userRole: ".concat(n)),console.log("proxySettings: ".concat(JSON.stringify(i))),l="App User"===n?s:i&&!0===i.DEFAULT_TEAM_DISABLED?s?[...s]:[m]:s?[...s,m]:[m],(0,a.jsxs)("div",{className:"mt-5 mb-5",children:[(0,a.jsx)(y.Z,{children:"Select Team"}),(0,a.jsx)(_.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,a.jsxs)(_.Z,{className:"mt-3 mb-3",children:[(0,a.jsx)("b",{children:"Default Team:"})," If no team_id is set for a key, it will be grouped under here."]}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>t(e),children:e.team_alias},l))}):(0,a.jsxs)(_.Z,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]})},ee=s(667),el=s(37963),es=s(97482);console.log=function(){},console.log("isLocal:",!1);var et=e=>{let{userID:l,userRole:s,teams:t,keys:n,setUserRole:o,userEmail:d,setUserEmail:c,setTeams:m,setKeys:p,premiumUser:j}=e,[g,Z]=(0,r.useState)(null),f=(0,i.useSearchParams)();f.get("viewSpend"),(0,i.useRouter)();let _=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),y=f.get("invitation_id"),[b,v]=(0,r.useState)(null),[k,S]=(0,r.useState)(null),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)(null),C={models:[],team_alias:"Default Team",team_id:null},[P,E]=(0,r.useState)(t?t[0]:C);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(_){let e=(0,el.o)(_);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),v(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"app_user":return"App User";case"internal_user":return"Internal User";case"internal_user_viewer":return"Internal Viewer";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),o(l)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&b&&s&&!n&&!g){let e=sessionStorage.getItem("userModels"+l);e?N(JSON.parse(e)):(async()=>{try{let e=await (0,u.Dj)(b);A(e);let t=await (0,u.Br)(b,l,s,!1,null,null);console.log("received teams in user dashboard: ".concat(Object.keys(t),"; team values: ").concat(Object.entries(t.teams))),Z(t.user_info),console.log("userSpendData: ".concat(JSON.stringify(g))),p(t.keys),m(t.teams);let n=[...t.teams];n.length>0?(console.log("response['teams']: ".concat(n)),E(n[0])):E(C),sessionStorage.setItem("userData"+l,JSON.stringify(t.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(t.user_info));let a=(await (0,u.So)(b,l,s)).data.map(e=>e.id);console.log("available_model_names:",a),N(a),console.log("userModels:",w),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,_,b,n,s]),(0,r.useEffect)(()=>{if(null!==n&&null!=P&&null!==P.team_id){let e=0;for(let l of n)P.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===P.team_id&&(e+=l.spend);S(e)}else if(null!==n){let e=0;for(let l of n)e+=l.spend;S(e)}},[P]),null!=y)return(0,a.jsx)(ee.default,{});if(null==l||null==_){let e="/sso/key/generate";return document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",console.log("Full URL:",e),window.location.href=e,null}if(null==b)return null;if(null==s&&o("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",P),(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)($,{userID:l,userRole:s,selectedTeam:P||null,accessToken:b}),(0,a.jsx)(X,{userID:l,userRole:s,userMaxBudget:(null==g?void 0:g.max_budget)||null,accessToken:b,userSpend:k,selectedTeam:P||null}),(0,a.jsx)(Y,{userID:l,userRole:s,accessToken:b,selectedTeam:P||null,data:n,setData:p,premiumUser:j,teams:t}),(0,a.jsx)(T,{userID:l,team:P||null,userRole:s,accessToken:b,data:n,setData:p},P?P.team_id:null),(0,a.jsx)(Q,{teams:t,setSelectedTeam:E,userRole:s,proxySettings:I,setProxySettings:A,userInfo:g,accessToken:b})]})})})},en=s(49167),ea=s(35087),er=s(92836),ei=s(26734),eo=s(41608),ed=s(32126),ec=s(23682),em=s(47047),eu=s(76628),eh=s(25707),ex=s(44041),ep=s(6180),ej=s(28683),eg=s(38302),eZ=s(66242),ef=s(78578),e_=s(34658),ey=e=>{let{modelID:l,accessToken:s}=e,[t,n]=(0,r.useState)(!1),i=async()=>{try{S.ZP.info("Making API Call"),n(!0);let e=await (0,u.Og)(s,l);console.log("model delete Response:",e),S.ZP.success("Model ".concat(l," deleted successfully")),n(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(U.Z,{onClick:()=>n(!0),icon:M.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:t,onOk:i,okType:"danger",onCancel:()=>n(!1),children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Delete Model"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Model ID: ",(0,a.jsx)("b",{children:l})]})})]})})]})},eb=s(97766),ev=s(46495),ek=s(18190),eS=s(91118),ew=e=>{let{modelMetrics:l,modelMetricsCategories:s,customTooltip:t,premiumUser:n}=e;return n?(0,a.jsx)(eS.Z,{title:"Time to First token (s)",className:"h-72",data:l,index:"date",showLegend:!1,categories:s,colors:["indigo","rose"],connectNulls:!0,customTooltip:t}):(0,a.jsxs)("div",{children:[(0,a.jsx)(ek.Z,{title:"✨ Enterprise Feature",color:"teal",className:"mt-2 mb-4",children:"Enterprise features are available for users with a specific license, please contact LiteLLM to unlock this limitation."}),(0,a.jsx)(p.Z,{variant:"primary",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get in touch"})})]})},eN=e=>{let{fields:l,selectedProvider:s}=e;return 0===l.length?null:(0,a.jsx)(a.Fragment,{children:l.map(e=>(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:e.field_name.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),name:e.field_name,tooltip:e.field_description,className:"mb-2",children:(0,a.jsx)(j.Z,{placeholder:e.field_value,type:"password"})},e.field_name))})},eI=s(67951);let{Title:eA,Link:eC}=es.default;(t=n||(n={})).OpenAI="OpenAI",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Studio",t.Anthropic="Anthropic",t.Google_AI_Studio="Google AI Studio",t.Bedrock="Amazon Bedrock",t.Groq="Groq",t.MistralAI="Mistral AI",t.Deepseek="Deepseek",t.OpenAI_Compatible="OpenAI-Compatible Endpoints (Together AI, etc.)",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.Cohere="Cohere",t.Databricks="Databricks",t.Ollama="Ollama";let eP={OpenAI:"openai",Azure:"azure",Azure_AI_Studio:"azure_ai",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",Groq:"groq",MistralAI:"mistral",Cohere:"cohere_chat",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai",Databricks:"databricks",Deepseek:"deepseek",Ollama:"ollama"},eT={"BadRequestError (400)":"BadRequestErrorRetries","AuthenticationError (401)":"AuthenticationErrorRetries","TimeoutError (408)":"TimeoutErrorRetries","RateLimitError (429)":"RateLimitErrorRetries","ContentPolicyViolationError (400)":"ContentPolicyViolationErrorRetries","InternalServerError (500)":"InternalServerErrorRetries"},eE=async(e,l,s)=>{try{let t=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(t)),console.log("received type of deployments: ".concat(typeof t)),t.forEach(async s=>{console.log("litellm_model: ".concat(s));let t={},n={};t.model=s;let a="";for(let[l,s]of(console.log("formValues add deployment:",e),Object.entries(e)))if(""!==s){if("model_name"==l)a+=s;else if("custom_llm_provider"==l){console.log("custom_llm_provider:",s);let e=eP[s];t.custom_llm_provider=e,console.log("custom_llm_provider mappingResult:",e)}else if("model"==l)continue;else if("base_model"===l)n[l]=s;else if("custom_model_name"===l)t.model=s;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",s);let e={};if(s&&void 0!=s){try{e=JSON.parse(s)}catch(e){throw S.ZP.error("Failed to parse LiteLLM Extra Params: "+e,10),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,s]of Object.entries(e))t[l]=s}}else t[l]=s}let r={model_name:a,litellm_params:t,model_info:n},i=await (0,u.kK)(l,r);console.log("response for model create call: ".concat(i.data))}),s.resetFields()}catch(e){S.ZP.error("Failed to create model: "+e,10)}};var eO=e=>{let l,{accessToken:s,token:t,userRole:i,userID:o,modelData:d={data:[]},keys:c,setModelData:m,premiumUser:h}=e,[g,Z]=(0,r.useState)([]),[f]=k.Z.useForm(),[b,v]=(0,r.useState)(null),[N,I]=(0,r.useState)(""),[P,T]=(0,r.useState)([]),E=Object.values(n).filter(e=>isNaN(Number(e))),[M,J]=(0,r.useState)([]),[Y,X]=(0,r.useState)("OpenAI"),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[et,ek]=(0,r.useState)(!1),[eS,eO]=(0,r.useState)(null),[eR,eF]=(0,r.useState)([]),[eM,eD]=(0,r.useState)([]),[eL,eU]=(0,r.useState)(null),[eV,ez]=(0,r.useState)([]),[eB,eq]=(0,r.useState)([]),[eK,eW]=(0,r.useState)([]),[eH,eG]=(0,r.useState)([]),[eJ,eY]=(0,r.useState)([]),[eX,e$]=(0,r.useState)([]),[eQ,e0]=(0,r.useState)([]),[e1,e2]=(0,r.useState)([]),[e4,e5]=(0,r.useState)([]),[e8,e3]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),[e6,e7]=(0,r.useState)(null),[e9,le]=(0,r.useState)(0),[ll,ls]=(0,r.useState)({}),[lt,ln]=(0,r.useState)([]),[la,lr]=(0,r.useState)(!1),[li,lo]=(0,r.useState)(null),[ld,lc]=(0,r.useState)(null),[lm,lu]=(0,r.useState)([]);(0,r.useEffect)(()=>{lS(eL,e8.from,e8.to)},[li,ld]);let lh=e=>{eO(e),el(!0)},lx=e=>{eO(e),ek(!0)},lp=async e=>{if(console.log("handleEditSubmit:",e),null==s)return;let l={},t=null;for(let[s,n]of(e.input_cost_per_million_tokens&&(e.input_cost_per_token=e.input_cost_per_million_tokens/1e6,delete e.input_cost_per_million_tokens),e.output_cost_per_million_tokens&&(e.output_cost_per_token=e.output_cost_per_million_tokens/1e6,delete e.output_cost_per_million_tokens),Object.entries(e)))"model_id"!==s?l[s]=n:t=n;let n={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",n);try{await (0,u.um)(s,n),S.ZP.success("Model updated successfully, restart server to see updates"),el(!1),eO(null)}catch(e){console.log("Error occurred")}},lj=()=>{I(new Date().toLocaleString())},lg=async()=>{if(!s){console.error("Access token is missing");return}console.log("new modelGroupRetryPolicy:",e6);try{await (0,u.K_)(s,{router_settings:{model_group_retry_policy:e6}}),S.ZP.success("Retry settings saved successfully")}catch(e){console.error("Failed to save retry settings:",e),S.ZP.error("Failed to save retry settings")}};if((0,r.useEffect)(()=>{if(!s||!t||!i||!o)return;let e=async()=>{try{var e,l,t,n,a,r,d,c,h,x,p,j;let g=await (0,u.hy)(s);J(g);let Z=await (0,u.AZ)(s,o,i);console.log("Model data response:",Z.data),m(Z);let f=new Set;for(let e=0;e0&&(y=_[_.length-1],console.log("_initial_model_group:",y)),console.log("selectedModelGroup:",eL);let b=await (0,u.o6)(s,o,i,y,null===(e=e8.from)||void 0===e?void 0:e.toISOString(),null===(l=e8.to)||void 0===l?void 0:l.toISOString(),null==li?void 0:li.token,ld);console.log("Model metrics response:",b),eq(b.data),eW(b.all_api_bases);let v=await (0,u.Rg)(s,y,null===(t=e8.from)||void 0===t?void 0:t.toISOString(),null===(n=e8.to)||void 0===n?void 0:n.toISOString());eG(v.data),eY(v.all_api_bases);let k=await (0,u.N8)(s,o,i,y,null===(a=e8.from)||void 0===a?void 0:a.toISOString(),null===(r=e8.to)||void 0===r?void 0:r.toISOString(),null==li?void 0:li.token,ld);console.log("Model exceptions response:",k),e$(k.data),e0(k.exception_types);let S=await (0,u.fP)(s,o,i,y,null===(d=e8.from)||void 0===d?void 0:d.toISOString(),null===(c=e8.to)||void 0===c?void 0:c.toISOString(),null==li?void 0:li.token,ld),w=await (0,u.n$)(s,null===(h=e8.from)||void 0===h?void 0:h.toISOString().split("T")[0],null===(x=e8.to)||void 0===x?void 0:x.toISOString().split("T")[0],y);ls(w);let N=await (0,u.v9)(s,null===(p=e8.from)||void 0===p?void 0:p.toISOString().split("T")[0],null===(j=e8.to)||void 0===j?void 0:j.toISOString().split("T")[0],y);ln(N),console.log("dailyExceptions:",w),console.log("dailyExceptionsPerDeplyment:",N),console.log("slowResponses:",S),e5(S);let I=await (0,u.j2)(s);lu(null==I?void 0:I.end_users);let A=(await (0,u.BL)(s,o,i)).router_settings;console.log("routerSettingsInfo:",A);let C=A.model_group_retry_policy,P=A.num_retries;console.log("model_group_retry_policy:",C),console.log("default_retries:",P),e7(C),le(P)}catch(e){console.error("There was an error fetching the model data",e)}};s&&t&&i&&o&&e();let l=async()=>{let e=await (0,u.qm)(s);console.log("received model cost map data: ".concat(Object.keys(e))),v(e)};null==b&&l(),lj()},[s,t,i,o,b,N]),!d||!s||!t||!i||!o)return(0,a.jsx)("div",{children:"Loading..."});let lZ=[],lf=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(b)),null!=b&&"object"==typeof b&&e in b)?b[e].litellm_provider:"openai";if(s){let e=s.split("/"),l=e[0];(a=t)||(a=1===e.length?u(s):l)}else a="-";n&&(r=null==n?void 0:n.input_cost_per_token,i=null==n?void 0:n.output_cost_per_token,o=null==n?void 0:n.max_tokens,c=null==n?void 0:n.max_input_tokens),(null==l?void 0:l.litellm_params)&&(m=Object.fromEntries(Object.entries(null==l?void 0:l.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),d.data[e].provider=a,d.data[e].input_cost=r,d.data[e].output_cost=i,d.data[e].litellm_model_name=s,lf.push(a),d.data[e].input_cost&&(d.data[e].input_cost=(1e6*Number(d.data[e].input_cost)).toFixed(2)),d.data[e].output_cost&&(d.data[e].output_cost=(1e6*Number(d.data[e].output_cost)).toFixed(2)),d.data[e].max_tokens=o,d.data[e].max_input_tokens=c,d.data[e].api_base=null==l?void 0:null===(lb=l.litellm_params)||void 0===lb?void 0:lb.api_base,d.data[e].cleanedLitellmParams=m,lZ.push(l.model_name),console.log(d.data[e])}if(d.data&&d.data.length>0&&d.data.sort((e,l)=>e.provider&&l.provider?e.provider.localeCompare(l.provider):e.provider&&!l.provider?-1:!e.provider&&l.provider?1:0),i&&"Admin Viewer"==i){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let lv=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(n).find(l=>n[l]===e);if(l){let e=eP[l];console.log("mappingResult: ".concat(e));let s=[];"object"==typeof b&&(Object.entries(b).forEach(l=>{let[t,n]=l;null!==n&&"object"==typeof n&&"litellm_provider"in n&&(n.litellm_provider===e||n.litellm_provider.includes(e))&&s.push(t)}),"Cohere"==l&&(console.log("adding cohere chat model"),Object.entries(b).forEach(e=>{let[l,t]=e;null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere"===t.litellm_provider&&s.push(l)}))),T(s),console.log("providerModels: ".concat(P))}},lk=async()=>{try{S.ZP.info("Running health check..."),Q("");let e=await (0,u.EY)(s);Q(e)}catch(e){console.error("Error running health check:",e),Q("Error running health check")}},lS=async(e,l,t)=>{if(console.log("Updating model metrics for group:",e),!s||!o||!i||!l||!t)return;console.log("inside updateModelMetrics - startTime:",l,"endTime:",t),eU(e);let n=null==li?void 0:li.token;void 0===n&&(n=null);let a=ld;void 0===a&&(a=null),l.setHours(0),l.setMinutes(0),l.setSeconds(0),t.setHours(23),t.setMinutes(59),t.setSeconds(59);try{let r=await (0,u.o6)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model metrics response:",r),eq(r.data),eW(r.all_api_bases);let d=await (0,u.Rg)(s,e,l.toISOString(),t.toISOString());eG(d.data),eY(d.all_api_bases);let c=await (0,u.N8)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model exceptions response:",c),e$(c.data),e0(c.exception_types);let m=await (0,u.fP)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);if(console.log("slowResponses:",m),e5(m),e){let n=await (0,u.n$)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ls(n);let a=await (0,u.v9)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ln(a)}}catch(e){console.error("Failed to fetch model metrics",e)}},lw=(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Select API Key Name"}),h?(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{lo(e)},children:e.key_alias},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lc(e)},children:e},l))]})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsxs)(G.Z,{value:String(l),disabled:!0,onClick:()=>{lo(e)},children:["✨ ",e.key_alias," (Enterprise only Feature)"]},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsxs)(G.Z,{value:e,disabled:!0,onClick:()=>{lc(e)},children:["✨ ",e," (Enterprise only Feature)"]},l))]})]})]}),lN=e=>{var l,s;let{payload:t,active:n}=e;if(!n||!t)return null;let r=null===(s=t[0])||void 0===s?void 0:null===(l=s.payload)||void 0===l?void 0:l.date,i=t.sort((e,l)=>l.value-e.value);if(i.length>5){let e=i.length-5;(i=i.slice(0,5)).push({dataKey:"".concat(e," other deployments"),value:t.slice(5).reduce((e,l)=>e+l.value,0),color:"gray"})}return(0,a.jsxs)("div",{className:"w-150 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r&&(0,a.jsxs)("p",{className:"text-tremor-content-emphasis mb-2",children:["Date: ",r]}),i.map((e,l)=>{let s=parseFloat(e.value.toFixed(5)),t=0===s&&e.value>0?"<0.00001":s.toFixed(5);return(0,a.jsxs)("div",{className:"flex justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("div",{className:"w-2 h-2 mt-1 rounded-full bg-".concat(e.color,"-500")}),(0,a.jsx)("p",{className:"text-tremor-content",children:e.dataKey})]}),(0,a.jsx)("p",{className:"font-medium text-tremor-content-emphasis text-righ ml-2",children:t})]},l)})]})},lI=e=>"Vertex AI (Anthropic, Gemini, etc.)"===e?"gemini-pro":"Anthropic"==e||"Amazon Bedrock"==e?"claude-3-opus":"Google AI Studio"==e?"gemini-pro":"Azure AI Studio"==e?"azure_ai/command-r-plus":"Azure"==e?"azure/my-deployment":"gpt-3.5-turbo";console.log("selectedProvider: ".concat(Y)),console.log("providerModels.length: ".concat(P.length));let lA=Object.keys(n).find(e=>n[e]===Y);return lA&&(l=M.find(e=>e.name===eP[lA])),(0,a.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(er.Z,{children:"All Models"}),(0,a.jsx)(er.Z,{children:"Add Model"}),(0,a.jsx)(er.Z,{children:(0,a.jsx)("pre",{children:"/health Models"})}),(0,a.jsx)(er.Z,{children:"Model Analytics"}),(0,a.jsx)(er.Z,{children:"Model Retry Settings"})]}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[N&&(0,a.jsxs)(_.Z,{children:["Last Refreshed: ",N]}),(0,a.jsx)(U.Z,{icon:F.Z,variant:"shadow",size:"xs",className:"self-center",onClick:lj})]})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsxs)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||void 0,onValueChange:e=>eU("all"===e?"all":e),value:eL||void 0,children:[(0,a.jsx)(G.Z,{value:"all",children:"All Models"}),eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))]})]}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{style:{maxWidth:"1500px",width:"100%"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Public Model Name"}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Provider"}),(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"LiteLLM Model"}),"Admin"===i&&(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"API Base"}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Input Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Output Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created At":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created At"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created By":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created By"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"50px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Status"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:d.data.filter(e=>"all"===eL||e.model_name===eL||null==eL||""===eL).map((e,l)=>{var t;return(0,a.jsxs)(W.Z,{style:{maxHeight:"1px",minHeight:"1px"},children:[(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.model_name||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.provider||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.litellm_model_name,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.litellm_model_name?e.litellm_model_name:"",children:e&&e.litellm_model_name?e.litellm_model_name.slice(0,20)+(e.litellm_model_name.length>20?"...":""):"-"})})}),"Admin"===i&&(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.api_base,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.api_base?e.api_base:"",children:e&&e.api_base?e.api_base.slice(0,20):"-"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.input_cost?e.input_cost:null!=e.litellm_params.input_cost_per_token&&void 0!=e.litellm_params.input_cost_per_token?(1e6*Number(e.litellm_params.input_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.output_cost?e.output_cost:e.litellm_params.output_cost_per_token?(1e6*Number(e.litellm_params.output_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&((t=e.model_info.created_at)?new Date(t).toLocaleDateString("en-US"):null)||"-"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&e.model_info.created_by||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:e.model_info.db_model?(0,a.jsx)(D.Z,{size:"xs",className:"text-white",children:(0,a.jsx)("p",{className:"text-xs",children:"DB Model"})}):(0,a.jsx)(D.Z,{size:"xs",className:"text-black",children:(0,a.jsx)("p",{className:"text-xs",children:"Config Model"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsxs)(x.Z,{numItems:3,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:O.Z,size:"sm",onClick:()=>lx(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>lh(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(ey,{modelID:e.model_info.id,accessToken:s})})]})})]},l)})})]})})]}),(0,a.jsx)(e=>{let{visible:l,onCancel:s,model:t,onSubmit:n}=e,[r]=k.Z.useForm(),i={},o="",d="";if(t){i=t.litellm_params,o=t.model_name;let e=t.model_info;e&&(d=e.id,console.log("model_id: ".concat(d)),i.model_id=d)}return(0,a.jsx)(w.Z,{title:"Edit Model "+o,visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n(e),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:lp,initialValues:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"organization",name:"organization",tooltip:"OpenAI Organization ID",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"tpm",name:"tpm",tooltip:"int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"rpm",name:"rpm",tooltip:"int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"max_retries",name:"max_retries",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"Input Cost per 1M Tokens",name:"input_cost_per_million_tokens",tooltip:"float (optional) - Input cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"Output Cost per 1M Tokens",name:"output_cost_per_million_tokens",tooltip:"float (optional) - Output cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:ee,onCancel:()=>{el(!1),eO(null)},model:eS,onSubmit:lp}),(0,a.jsxs)(w.Z,{title:eS&&eS.model_name,visible:et,width:800,footer:null,onCancel:()=>{ek(!1),eO(null)},children:[(0,a.jsx)(y.Z,{children:"Model Info"}),(0,a.jsx)(eI.Z,{language:"json",children:eS&&JSON.stringify(eS,null,2)})]})]}),(0,a.jsxs)(ed.Z,{className:"h-full",children:[(0,a.jsx)(eA,{level:2,children:"Add new model"}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(k.Z,{form:f,onFinish:()=>{f.validateFields().then(e=>{eE(e,s,f)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc.",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:Y.toString(),children:E.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lv(e),X(e)},children:e},l))})}),(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Public Model Name",name:"model_name",tooltip:"Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name.",className:"mb-0",children:(0,a.jsx)(j.Z,{})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,a.jsxs)(k.Z.Item,{label:"LiteLLM Model Name(s)",tooltip:"Actual model name used for making litellm.completion() / litellm.embedding() call.",className:"mb-0",children:[(0,a.jsx)(k.Z.Item,{name:"model",rules:[{required:!0,message:"Required"}],noStyle:!0,children:"Azure"===Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"===Y||"Ollama"===Y?(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())}):P.length>0?(0,a.jsxs)(em.Z,{children:[(0,a.jsx)(eu.Z,{value:"custom",children:"Custom Model Name (Enter below)"}),P.map((e,l)=>(0,a.jsx)(eu.Z,{value:e,children:e},l))]}):(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())})}),(0,a.jsx)(k.Z.Item,{noStyle:!0,shouldUpdate:(e,l)=>e.model!==l.model,children:e=>{let{getFieldValue:l}=e;return(l("model")||[]).includes("custom")&&(0,a.jsx)(k.Z.Item,{name:"custom_model_name",rules:[{required:!0,message:"Please enter a custom model name"}],className:"mt-2",children:(0,a.jsx)(j.Z,{placeholder:"Enter custom model name"})})}})]}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Actual model name used for making"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," ","models with the same 'public name'"]})})]}),void 0!==l&&l.fields.length>0&&(0,a.jsx)(eN,{fields:l.fields,selectedProvider:l.name}),"Amazon Bedrock"!=Y&&"Vertex AI (Anthropic, Gemini, etc.)"!=Y&&"Ollama"!=Y&&(void 0===l||0==l.fields.length)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,a.jsx)(j.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==Y&&(0,a.jsx)(k.Z.Item,{label:"Organization ID",name:"organization",children:(0,a.jsx)(j.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,a.jsx)(j.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,a.jsx)(ev.Z,{name:"file",accept:".json",beforeUpload:e=>{if("application/json"===e.type){let l=new FileReader;l.onload=e=>{if(e.target){let l=e.target.result;f.setFieldsValue({vertex_credentials:l})}},l.readAsText(e)}return!1},onChange(e){"uploading"!==e.file.status&&console.log(e.file,e.fileList),"done"===e.file.status?S.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&S.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,a.jsx)(C.ZP,{icon:(0,a.jsx)(eb.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"==Y)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,a.jsx)(j.Z,{placeholder:"https://..."})}),"Azure"==Y&&(0,a.jsx)(k.Z.Item,{label:"API Version",name:"api_version",tooltip:"By default litellm will use the latest version. If you want to use a different version, you can specify it here",children:(0,a.jsx)(j.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==Y&&(0,a.jsxs)("div",{children:[(0,a.jsx)(k.Z.Item,{label:"Base Model",name:"base_model",className:"mb-0",children:(0,a.jsx)(j.Z,{placeholder:"azure/gpt-3.5-turbo"})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-2",children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from"," ",(0,a.jsx)(eC,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})})]})]}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Access Key ID",name:"aws_access_key_id",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Secret Access Key",name:"aws_secret_access_key",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Region Name",name:"aws_region_name",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),(0,a.jsx)(k.Z.Item,{label:"LiteLLM Params",name:"litellm_extra_params",tooltip:"Optional litellm params used for making a litellm.completion() call.",className:"mb-0",children:(0,a.jsx)(ef.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,a.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Model"})}),(0,a.jsx)(ep.Z,{title:"Get help on our github",children:(0,a.jsx)(es.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,a.jsx)(p.Z,{onClick:lk,children:"Run `/health`"}),$&&(0,a.jsx)("pre",{children:JSON.stringify($,null,2)})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:4,className:"mt-2 mb-2",children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:e8,className:"mr-2",onValueChange:e=>{e3(e),lS(eL,e.from,e.to)}})]}),(0,a.jsxs)(ej.Z,{className:"ml-2",children:[(0,a.jsx)(_.Z,{children:"Select Model Group"}),(0,a.jsx)(H.Z,{defaultValue:eL||eR[0],value:eL||eR[0],children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>lS(e,e8.from,e8.to),children:e},l))})]}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(eZ.Z,{trigger:"click",content:lw,overlayStyle:{width:"20vw"},children:(0,a.jsx)(p.Z,{icon:e_.Z,size:"md",variant:"secondary",className:"mt-4 ml-2",style:{border:"none"},onClick:()=>lr(!0)})})})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"mr-2 max-h-[400px] min-h-[400px]",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Avg. Latency per Token"}),(0,a.jsx)(er.Z,{value:"2",children:"✨ Time to first token"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("p",{className:"text-gray-500 italic",children:" (seconds/token)"}),(0,a.jsx)(_.Z,{className:"text-gray-500 italic mt-1 mb-1",children:"average Latency for successfull requests divided by the total tokens"}),eB&&eK&&(0,a.jsx)(eh.Z,{title:"Model Latency",className:"h-72",data:eB,showLegend:!1,index:"date",categories:eK,connectNulls:!0,customTooltip:lN})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(ew,{modelMetrics:eH,modelMetricsCategories:eJ,customTooltip:lN,premiumUser:h})})]})]})})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"ml-2 max-h-[400px] min-h-[400px] overflow-y-auto",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Deployment"}),(0,a.jsx)(K.Z,{children:"Success Responses"}),(0,a.jsxs)(K.Z,{children:["Slow Responses ",(0,a.jsx)("p",{children:"Success Responses taking 600+s"})]})]})}),(0,a.jsx)(z.Z,{children:e4.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.api_base}),(0,a.jsx)(B.Z,{children:e.total_count}),(0,a.jsx)(B.Z,{children:e.slow_count})]},l))})]})})})]}),(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Exceptions for ",eL]}),(0,a.jsx)(ex.Z,{className:"h-60",data:eX,index:"model",categories:eQ,stack:!0,yAxisWidth:30})]})}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Up Rate Limit Errors (429) for ",eL]}),(0,a.jsxs)(x.Z,{numItems:1,children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",ll.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:ll.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]}),(0,a.jsx)(ej.Z,{})]})]}),h?(0,a.jsx)(a.Fragment,{children:lt.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base?e.api_base:"Unknown API Base"}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors (429) ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]},l))}):(0,a.jsx)(a.Fragment,{children:lt&<.length>0&<.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Rate Limit Errors by Deployment"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see exceptions for all deployments"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]})]},l))})]})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsx)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||eR[0],value:eL||eR[0],onValueChange:e=>eU(e),children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))})]}),(0,a.jsxs)(y.Z,{children:["Retry Policy for ",eL]}),(0,a.jsx)(_.Z,{className:"mb-6",children:"How many retries should be attempted based on the Exception"}),eT&&(0,a.jsx)("table",{children:(0,a.jsx)("tbody",{children:Object.entries(eT).map((e,l)=>{var s;let[t,n]=e,r=null==e6?void 0:null===(s=e6[eL])||void 0===s?void 0:s[n];return null==r&&(r=e9),(0,a.jsxs)("tr",{className:"flex justify-between items-center mt-2",children:[(0,a.jsx)("td",{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)("td",{children:(0,a.jsx)(A.Z,{className:"ml-5",value:r,min:0,step:1,onChange:e=>{e7(l=>{var s;let t=null!==(s=null==l?void 0:l[eL])&&void 0!==s?s:{};return{...null!=l?l:{},[eL]:{...t,[n]:e}}})}})})]},l)})})}),(0,a.jsx)(p.Z,{className:"mt-6 mr-8",onClick:lg,children:"Save"})]})]})]})})},eR=e=>{let{isInvitationLinkModalVisible:l,setIsInvitationLinkModalVisible:s,baseUrl:t,invitationLinkData:n}=e,{Title:r,Paragraph:i}=es.default,o=()=>(null==n?void 0:n.has_user_setup_sso)?"".concat(t,"/ui"):"".concat(t,"/ui?invitation_id=").concat(null==n?void 0:n.id);return(0,a.jsxs)(w.Z,{title:"Invitation Link",visible:l,width:800,footer:null,onOk:()=>{s(!1)},onCancel:()=>{s(!1)},children:[(0,a.jsx)(i,{children:"Copy and send the generated link to onboard this user to the proxy."}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"User ID"}),(0,a.jsx)(_.Z,{children:null==n?void 0:n.user_id})]}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{children:"Invitation Link"}),(0,a.jsx)(_.Z,{children:(0,a.jsx)(_.Z,{children:o()})})]}),(0,a.jsxs)("div",{className:"flex justify-end mt-5",children:[(0,a.jsx)("div",{}),(0,a.jsx)(b.CopyToClipboard,{text:o(),onCopy:()=>S.ZP.success("Copied!"),children:(0,a.jsx)(p.Z,{variant:"primary",children:"Copy invitation link"})})]})]})};let{Option:eF}=v.default;var eM=e=>{let{userID:l,accessToken:s,teams:t,possibleUIRoles:n}=e,[o,d]=(0,r.useState)(null),[c]=k.Z.useForm(),[m,h]=(0,r.useState)(!1),[x,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),[y,b]=(0,r.useState)(!1),[N,A]=(0,r.useState)(null),P=(0,i.useRouter)();console.log=function(){};let[T,E]=(0,r.useState)("");(0,r.useEffect)(()=>{(async()=>{try{let e=await (0,u.So)(s,l,"any"),t=[];for(let l=0;l{if(P){let{protocol:e,host:l}=window.location;E("".concat(e,"/").concat(l))}},[P]);let O=async e=>{try{var t;S.ZP.info("Making API Call"),h(!0),console.log("formValues in create user:",e);let n=await (0,u.Ov)(s,null,e);console.log("user create Response:",n),g(n.key);let a=(null===(t=n.data)||void 0===t?void 0:t.user_id)||n.user_id;if(null==o?void 0:o.SSO_ENABLED){let e={id:crypto.randomUUID(),user_id:a,is_accepted:!1,accepted_at:null,expires_at:new Date(Date.now()+6048e5),created_at:new Date,created_by:l,updated_at:new Date,updated_by:l,has_user_setup_sso:!0};A(e),b(!0)}else(0,u.XO)(s,a).then(e=>{e.has_user_setup_sso=!1,A(e),b(!0)});S.ZP.success("API user Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-0",onClick:()=>h(!0),children:"+ Invite User"}),(0,a.jsxs)(w.Z,{title:"Invite User",visible:m,width:800,footer:null,onOk:()=>{h(!1),c.resetFields()},onCancel:()=>{h(!1),g(null),c.resetFields()},children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Create a User who can own keys"}),(0,a.jsxs)(k.Z,{form:c,onFinish:O,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(k.Z.Item,{label:"User Email",name:"user_email",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:n&&Object.entries(n).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,a.jsx)(v.default,{placeholder:"Select Team ID",style:{width:"100%"},children:t?t.map(e=>(0,a.jsx)(eF,{value:e.team_id,children:e.team_alias},e.team_id)):(0,a.jsx)(eF,{value:null,children:"Default Team"},"default")})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create User"})})]})]}),x&&(0,a.jsx)(eR,{isInvitationLinkModalVisible:y,setIsInvitationLinkModalVisible:b,baseUrl:T,invitationLinkData:N})]})},eD=e=>{let{visible:l,possibleUIRoles:s,onCancel:t,user:n,onSubmit:i}=e,[o,d]=(0,r.useState)(n),[c]=k.Z.useForm();(0,r.useEffect)(()=>{c.resetFields()},[n]);let m=async()=>{c.resetFields(),t()},u=async e=>{i(e),c.resetFields(),t()};return n?(0,a.jsx)(w.Z,{visible:l,onCancel:m,footer:null,title:"Edit User "+n.user_id,width:1e3,children:(0,a.jsx)(k.Z,{form:c,onFinish:u,initialValues:n,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"User Email",tooltip:"Email of the User",name:"user_email",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"user_id",name:"user_id",hidden:!0,children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:s&&Object.entries(s).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Spend (USD)",name:"spend",tooltip:"(float) - Spend of all LLM calls completed by this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"User Budget (USD)",name:"max_budget",tooltip:"(float) - Maximum budget of this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})}):null};console.log=function(){};var eL=e=>{let{accessToken:l,token:s,keys:t,userRole:n,userID:i,teams:o,setKeys:d}=e,[c,m]=(0,r.useState)(null),[h,p]=(0,r.useState)(null),[j,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)(1),[_,y]=r.useState(null),[b,v]=(0,r.useState)(null),[k,w]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[A,C]=(0,r.useState)({}),P=async()=>{I(null),w(!1)},T=async e=>{if(console.log("inside handleEditSubmit:",e),l&&s&&n&&i){try{await (0,u.pf)(l,e,null),S.ZP.success("User ".concat(e.user_id," updated successfully"))}catch(e){console.error("There was an error updating the user",e)}h&&p(h.map(l=>l.user_id===e.user_id?e:l)),I(null),w(!1)}};return((0,r.useEffect)(()=>{if(!l||!s||!n||!i)return;let e=async()=>{try{let e=await (0,u.Br)(l,null,n,!0,Z,25);m(e),console.log("user data response:",e),p(e.users||[]);let s=await (0,u.lg)(l);C(s)}catch(e){console.error("There was an error fetching the model data",e)}};l&&s&&n&&i&&e()},[l,s,n,i,Z]),h&&l&&s&&n&&i)?(0,a.jsx)("div",{style:{width:"100%"},children:(0,a.jsxs)(x.Z,{className:"gap-2 p-2 h-[90vh] w-full mt-8",children:[(0,a.jsx)(eM,{userID:i,accessToken:l,teams:o,possibleUIRoles:A}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[90vh] mb-4",children:[(0,a.jsx)("div",{className:"mb-4 mt-1"}),(0,a.jsx)(ei.Z,{children:(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(V.Z,{className:"mt-5",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"User ID"}),(0,a.jsx)(K.Z,{children:"User Email"}),(0,a.jsx)(K.Z,{children:"Role"}),(0,a.jsx)(K.Z,{children:"User Spend ($ USD)"}),(0,a.jsx)(K.Z,{children:"User Max Budget ($ USD)"}),(0,a.jsx)(K.Z,{children:"API Keys"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:h.map(e=>{var l,s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_id||"-"}),(0,a.jsx)(B.Z,{children:e.user_email||"-"}),(0,a.jsx)(B.Z,{children:(null==A?void 0:null===(l=A[null==e?void 0:e.user_role])||void 0===l?void 0:l.ui_label)||"-"}),(0,a.jsx)(B.Z,{children:e.spend?null===(s=e.spend)||void 0===s?void 0:s.toFixed(2):"-"}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(x.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,a.jsxs)(D.Z,{size:"xs",color:"indigo",children:[e.key_aliases.filter(e=>null!==e).length,"\xa0Keys"]}):(0,a.jsx)(D.Z,{size:"xs",color:"gray",children:"No Keys"})})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,onClick:()=>{I(e),w(!0)},children:"View Keys"})})]},e.user_id)})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"flex-1"}),(0,a.jsx)("div",{className:"flex-1 flex justify-between items-center"})]})})]})}),(0,a.jsx)(eD,{visible:k,possibleUIRoles:A,onCancel:P,user:N,onSubmit:T})]}),function(){if(!h)return null;let e=(null==c?void 0:c.total_pages)||0,l=e=>{p([]),f(e)};return(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsxs)("div",{children:["Showing Page ",Z," of ",e]}),(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===Z,onClick:()=>l(Z-1),children:"← Prev"}),(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:Z===e,onClick:()=>l(Z+1),children:"Next →"})]})]})}()]})}):(0,a.jsx)("div",{children:"Loading..."})};console.log=function(){};var eU=e=>{let{teams:l,searchParams:s,accessToken:t,setTeams:n,userID:i,userRole:o}=e;(0,r.useEffect)(()=>{console.log("inside useeffect - ".concat(l)),null===l&&t&&(async()=>{let e=await (0,u.It)(t);console.log("givenTeams: ".concat(e)),n(e)})()},[l]);let[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:g}=es.default,[Z,f]=(0,r.useState)(""),[y,b]=(0,r.useState)(!1),[N,P]=(0,r.useState)(l?l[0]:null),[T,E]=(0,r.useState)(!1),[O,F]=(0,r.useState)(!1),[J,Y]=(0,r.useState)([]),[X,$]=(0,r.useState)(!1),[Q,ee]=(0,r.useState)(null),[el,et]=(0,r.useState)({}),en=e=>{P(e),b(!0)},ea=async e=>{let s=e.team_id;if(console.log("handleEditSubmit:",e),null==t)return;let a=await (0,u.Gh)(t,e);l&&n(l.map(e=>e.team_id===s?a.data:e)),S.ZP.success("Team updated successfully"),b(!1),P(null)},er=async e=>{ee(e),$(!0)},ei=async()=>{if(null!=Q&&null!=l&&null!=t){try{await (0,u.rs)(t,Q);let e=l.filter(e=>e.team_id!==Q);n(e)}catch(e){console.error("Error deleting the team:",e)}$(!1),ee(null)}};(0,r.useEffect)(()=>{let e=async()=>{try{if(null===i||null===o||null===t||null===l)return;let e={},s=await (0,u.It)(t);for(let l=0;l{try{if(null===i||null===o)return;if(null!==t){let e=(await (0,u.So)(t,i,o)).data.map(e=>e.id);console.log("available_model_names:",e),Y(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,i,o,l]);let eo=async e=>{try{if(null!=t){var s;let a=null==e?void 0:e.team_alias;if((null!==(s=null==l?void 0:l.map(e=>e.team_alias))&&void 0!==s?s:[]).includes(a))throw Error("Team alias ".concat(a," already exists, please pick another alias"));S.ZP.info("Creating Team");let r=await (0,u.hT)(t,e);null!==l?n([...l,r]):n([r]),console.log("response for team create call: ".concat(r)),S.ZP.success("Team created"),E(!1)}}catch(e){console.error("Error creating the team:",e),S.ZP.error("Error creating the team: "+e,20)}},ed=async e=>{try{if(null!=t&&null!=l){S.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},a=await (0,u.cu)(t,N.team_id,s);console.log("response for team create call: ".concat(a.data));let r=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(a.data.team_id)),e.team_id===a.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...l];e[r]=a.data,n(e),P(a.data)}F(!1)}}catch(e){console.error("Error creating the team:",e)}};return(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"All Teams"}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Team Name"}),(0,a.jsx)(K.Z,{children:"Team ID"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"TPM / RPM Limits"}),(0,a.jsx)(K.Z,{children:"Info"})]})}),(0,a.jsx)(z.Z,{children:l&&l.length>0?l.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",fontSize:"0.75em"},children:(0,a.jsx)(ep.Z,{title:e.team_id,children:e.team_id})}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!==e.max_budget&&void 0!==e.max_budget?e.max_budget:"No limit"}),(0,a.jsx)(B.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].keys&&el[e.team_id].keys.length," ","Keys"]}),(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].team_info&&el[e.team_id].team_info.members_with_roles&&el[e.team_id].team_info.members_with_roles.length," ","Members"]})]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>en(e)}),(0,a.jsx)(U.Z,{onClick:()=>er(e.team_id),icon:M.Z,size:"sm"})]})]},e.team_id)):null})]}),X&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:ei,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{$(!1),ee(null)},children:"Cancel"})]})]})]})})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>E(!0),children:"+ Create New Team"}),(0,a.jsx)(w.Z,{title:"Create Team",visible:T,width:800,footer:null,onOk:()=>{E(!1),d.resetFields()},onCancel:()=>{E(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:eo,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"Team Members"}),(0,a.jsx)(g,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{P(e)},children:e.team_alias},l))}):(0,a.jsxs)(g,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:N?N.members_with_roles.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsx)(B.Z,{children:e.role})]},l)):null})]})}),N&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,team:t,onSubmit:n}=e,[r]=k.Z.useForm();return(0,a.jsx)(w.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n({...e,team_id:t.team_id}),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:ea,initialValues:t,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J&&J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:y,onCancel:()=>{b(!1),P(null)},team:N,onSubmit:ea})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-5",onClick:()=>F(!0),children:"+ Add member"}),(0,a.jsx)(w.Z,{title:"Add member",visible:O,width:800,footer:null,onOk:()=>{F(!1),c.resetFields()},onCancel:()=>{F(!1),c.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:ed,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,a.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,a.jsx)(k.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eV=e=>{let l,{searchParams:s,accessToken:t,showSSOBanner:n,premiumUser:o}=e,[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:j}=es.default,[g,Z]=(0,r.useState)(""),[f,y]=(0,r.useState)(null),[b,v]=(0,r.useState)(null),[N,A]=(0,r.useState)(!1),[P,T]=(0,r.useState)(!1),[E,O]=(0,r.useState)(!1),[F,M]=(0,r.useState)(!1),[D,J]=(0,r.useState)(!1),[Y,X]=(0,r.useState)(!1),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(!1),[et,en]=(0,r.useState)(!1),[ea,er]=(0,r.useState)([]),[ei,eo]=(0,r.useState)(null),ed=(0,i.useRouter)(),[ec,em]=(0,r.useState)(null);console.log=function(){};let[eu,eh]=(0,r.useState)(""),ex="All IP Addresses Allowed";try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let ep=async()=>{try{if(!0!==o){S.ZP.error("This feature is only available for premium users. Please upgrade your account.");return}if(t){let e=await (0,u.PT)(t);er(e&&e.length>0?e:[ex])}else er([ex])}catch(e){console.error("Error fetching allowed IPs:",e),S.ZP.error("Failed to fetch allowed IPs ".concat(e)),er([ex])}finally{!0===o&&Q(!0)}},ej=async e=>{try{if(t){await (0,u.eH)(t,e.ip);let l=await (0,u.PT)(t);er(l),S.ZP.success("IP address added successfully")}}catch(e){console.error("Error adding IP:",e),S.ZP.error("Failed to add IP address ".concat(e))}finally{el(!1)}},eg=async e=>{eo(e),en(!0)},eZ=async()=>{if(ei&&t)try{await (0,u.$I)(t,ei);let e=await (0,u.PT)(t);er(e.length>0?e:[ex]),S.ZP.success("IP address deleted successfully")}catch(e){console.error("Error deleting IP:",e),S.ZP.error("Failed to delete IP address ".concat(e))}finally{en(!1),eo(null)}},ef=()=>{X(!1)},e_=["proxy_admin","proxy_admin_viewer"];(0,r.useEffect)(()=>{if(ed){let{protocol:e,host:l}=window.location;eh("".concat(e,"//").concat(l))}},[ed]),(0,r.useEffect)(()=>{(async()=>{if(null!=t){let e=[],l=await (0,u.Xd)(t,"proxy_admin_viewer");console.log("proxy admin viewer response: ",l);let s=l.users;console.log("proxy viewers response: ".concat(s)),s.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(s));let n=(await (0,u.Xd)(t,"proxy_admin")).users;n.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(n)),console.log("combinedList: ".concat(e)),y(e),em(await (0,u.lg)(t))}})()},[t]);let ey=()=>{M(!1),c.resetFields(),d.resetFields()},eb=()=>{M(!1),c.resetFields(),d.resetFields()},ev=e=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-8 mt-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},className:"mt-4",children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]}),eS=(e,l,s)=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:l,children:e_.map((e,l)=>(0,a.jsx)(G.Z,{value:e,children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:s,valuePropName:"user_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s,disabled:!0})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update role"})})]}),ew=async e=>{try{if(null!=t&&null!=f){S.ZP.info("Making API Call");let l=await (0,u.pf)(t,e,null);console.log("response for team create call: ".concat(l));let s=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(l.user_id)),e.user_id===l.user_id));console.log("foundIndex: ".concat(s)),-1==s&&(console.log("updates admin with new user"),f.push(l),y(f)),S.ZP.success("Refresh tab to see updated user role"),M(!1)}}catch(e){console.error("Error creating the key:",e)}},eN=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call");let s=await (0,u.pf)(t,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(s));let n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)});let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(s.user_id)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),T(!1)}}catch(e){console.error("Error creating the key:",e)}},eI=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call"),e.user_email,e.user_id;let s=await (0,u.pf)(t,e,"proxy_admin"),n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)}),console.log("response for team create call: ".concat(s));let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(n)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),O(!1)}}catch(e){console.error("Error creating the key:",e)}},eA=async e=>{if(null==t)return;let l={environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}};(0,u.K_)(t,l)};return console.log("admins: ".concat(null==f?void 0:f.length)),(0,a.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,a.jsx)(m,{level:4,children:"Admin Access "}),(0,a.jsxs)(j,{children:[n&&(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc."," ",(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:f?f.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsxs)(B.Z,{children:[" ",(null==ec?void 0:null===(s=ec[null==e?void 0:e.user_role])||void 0===s?void 0:s.ui_label)||"-"]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>M(!0)}),(0,a.jsx)(w.Z,{title:"Update role",visible:F,width:800,footer:null,onOk:ey,onCancel:eb,children:eS(ew,e.user_role,e.user_id)})]})]},l)}):null})]})})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("div",{className:"flex justify-start",children:[(0,a.jsx)(p.Z,{className:"mr-4 mb-5",onClick:()=>O(!0),children:"+ Add admin"}),(0,a.jsx)(w.Z,{title:"Add admin",visible:E,width:800,footer:null,onOk:()=>{O(!1),c.resetFields(),d.resetFields()},onCancel:()=>{O(!1),A(!1),c.resetFields(),d.resetFields()},children:ev(eI)}),(0,a.jsx)(eR,{isInvitationLinkModalVisible:N,setIsInvitationLinkModalVisible:A,baseUrl:eu,invitationLinkData:b}),(0,a.jsx)(p.Z,{className:"mb-5",onClick:()=>T(!0),children:"+ Add viewer"}),(0,a.jsx)(w.Z,{title:"Add viewer",visible:P,width:800,footer:null,onOk:()=>{T(!1),c.resetFields(),d.resetFields()},onCancel:()=>{T(!1),c.resetFields(),d.resetFields()},children:ev(eN)})]})})]}),(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(m,{level:4,children:" ✨ Security Settings"}),(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem",marginTop:"1rem"},children:[(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:()=>!0===o?J(!0):S.ZP.error("Only premium users can add SSO"),children:"Add SSO"})}),(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:ep,children:"Allowed IPs"})})]})]}),(0,a.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,a.jsx)(w.Z,{title:"Add SSO",visible:D,width:800,footer:null,onOk:()=>{J(!1),d.resetFields()},onCancel:()=>{J(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:e=>{eI(e),eA(e),J(!1),X(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,a.jsx)(I.Z.Password,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,a.jsx)(I.Z.Password,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,a.jsxs)(w.Z,{title:"SSO Setup Instructions",visible:Y,width:800,footer:null,onOk:ef,onCancel:()=>{X(!1)},children:[(0,a.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{onClick:ef,children:"Done"})})]}),(0,a.jsx)(w.Z,{title:"Manage Allowed IP Addresses",width:800,visible:$,onCancel:()=>Q(!1),footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>el(!0),children:"Add IP Address"},"add"),(0,a.jsx)(p.Z,{onClick:()=>Q(!1),children:"Close"},"close")],children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"IP Address"}),(0,a.jsx)(K.Z,{className:"text-right",children:"Action"})]})}),(0,a.jsx)(z.Z,{children:ea.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e}),(0,a.jsx)(B.Z,{className:"text-right",children:e!==ex&&(0,a.jsx)(p.Z,{onClick:()=>eg(e),color:"red",size:"xs",children:"Delete"})})]},l))})]})}),(0,a.jsx)(w.Z,{title:"Add Allowed IP Address",visible:ee,onCancel:()=>el(!1),footer:null,children:(0,a.jsxs)(k.Z,{onFinish:ej,children:[(0,a.jsx)(k.Z.Item,{name:"ip",rules:[{required:!0,message:"Please enter an IP address"}],children:(0,a.jsx)(I.Z,{placeholder:"Enter IP address"})}),(0,a.jsx)(k.Z.Item,{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add IP Address"})})]})}),(0,a.jsx)(w.Z,{title:"Confirm Delete",visible:et,onCancel:()=>en(!1),onOk:eZ,footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>eZ(),children:"Yes"},"delete"),(0,a.jsx)(p.Z,{onClick:()=>en(!1),children:"Close"},"close")],children:(0,a.jsxs)("p",{children:["Are you sure you want to delete the IP address: ",ei,"?"]})})]}),(0,a.jsxs)(ek.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access"," ",(0,a.jsxs)("a",{href:l,target:"_blank",children:[(0,a.jsx)("b",{children:l})," "]})]})]})]})},ez=s(42556),eB=s(90252),eq=e=>{let{alertingSettings:l,handleInputChange:s,handleResetField:t,handleSubmit:n,premiumUser:r}=e,[i]=k.Z.useForm();return(0,a.jsxs)(k.Z,{form:i,onFinish:()=>{console.log("INSIDE ONFINISH");let e=i.getFieldsValue(),l=Object.entries(e).every(e=>{let[l,s]=e;return"boolean"!=typeof s&&(""===s||null==s)});console.log("formData: ".concat(JSON.stringify(e),", isEmpty: ").concat(l)),l?console.log("Some form fields are empty."):n(e)},labelAlign:"left",children:[l.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{align:"center",children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?r?(0,a.jsx)(k.Z.Item,{name:e.field_name,children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l)}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>s(e.field_name,l)}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}):(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,a.jsx)(k.Z.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l),className:"p-0"}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>{s(e.field_name,l),i.setFieldsValue({[e.field_name]:l})}}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>t(e.field_name,l),children:"Reset"})})]},l)),(0,a.jsx)("div",{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update Settings"})})]})},eK=e=>{let{accessToken:l,premiumUser:s}=e,[t,n]=(0,r.useState)([]);return(0,r.useEffect)(()=>{l&&(0,u.RQ)(l).then(e=>{n(e)})},[l]),(0,a.jsx)(eq,{alertingSettings:t,handleInputChange:(e,l)=>{let s=t.map(s=>s.field_name===e?{...s,field_value:l}:s);console.log("updatedSettings: ".concat(JSON.stringify(s))),n(s)},handleResetField:(e,s)=>{if(l)try{let l=t.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:l.field_default_value}:l);n(l)}catch(e){console.log("ERROR OCCURRED!")}},handleSubmit:e=>{if(!l||(console.log("formValues: ".concat(e)),null==e||void 0==e))return;let s={};t.forEach(e=>{s[e.field_name]=e.field_value});let n={...e,...s};console.log("mergedFormValues: ".concat(JSON.stringify(n)));let{slack_alerting:a,...r}=n;console.log("slack_alerting: ".concat(a,", alertingArgs: ").concat(JSON.stringify(r)));try{(0,u.jA)(l,"alerting_args",r),"boolean"==typeof a&&(!0==a?(0,u.jA)(l,"alerting",["slack"]):(0,u.jA)(l,"alerting",[])),S.ZP.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:s})},eW=s(84406);let{Title:eH,Paragraph:eG}=es.default;console.log=function(){};var eJ=e=>{let{accessToken:l,userRole:s,userID:t,premiumUser:n}=e,[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)([]),[m,h]=(0,r.useState)(!1),[g]=k.Z.useForm(),[Z,f]=(0,r.useState)(null),[y,b]=(0,r.useState)([]),[N,I]=(0,r.useState)(""),[A,P]=(0,r.useState)({}),[T,E]=(0,r.useState)([]),[O,F]=(0,r.useState)(!1),[M,D]=(0,r.useState)([]),[H,J]=(0,r.useState)(null),[Y,X]=(0,r.useState)([]),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(null),es=e=>{T.includes(e)?E(T.filter(l=>l!==e)):E([...T,e])},et={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,r.useEffect)(()=>{l&&s&&t&&(0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e),o(e.callbacks),D(e.available_callbacks);let l=e.alerts;if(console.log("alerts_data",l),l&&l.length>0){let e=l[0];console.log("_alert_info",e);let s=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",s),E(e.active_alerts),I(s),P(e.alerts_to_webhook)}c(l)})},[l,s,t]);let en=e=>T&&T.includes(e),ea=()=>{if(!l)return;let e={};d.filter(e=>"email"===e.name).forEach(l=>{var s;Object.entries(null!==(s=l.variables)&&void 0!==s?s:{}).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));n&&n.value&&(e[s]=null==n?void 0:n.value)})}),console.log("updatedVariables",e);try{(0,u.K_)(l,{general_settings:{alerting:["email"]},environment_variables:e})}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Email settings updated successfully")},em=async e=>{if(!l)return;let s={};Object.entries(e).forEach(e=>{let[l,t]=e;"callback"!==l&&(s[l]=t)});try{await (0,u.K_)(l,{environment_variables:s}),S.ZP.success("Callback added successfully"),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eu=async e=>{if(!l)return;let s=null==e?void 0:e.callback,t={};Object.entries(e).forEach(e=>{let[l,s]=e;"callback"!==l&&(t[l]=s)});try{await (0,u.K_)(l,{environment_variables:t,litellm_settings:{success_callback:[s]}}),S.ZP.success("Callback ".concat(s," added successfully")),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eh=e=>{console.log("inside handleSelectedCallbackChange",e),f(e.litellm_callback_name),console.log("all callbacks",M),e&&e.litellm_callback_params?(X(e.litellm_callback_params),console.log("selectedCallbackParams",Y)):X([])};return l?(console.log("callbacks: ".concat(i)),(0,a.jsxs)("div",{className:"w-full mx-4",children:[(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Logging Callbacks"}),(0,a.jsx)(er.Z,{value:"2",children:"Alerting Types"}),(0,a.jsx)(er.Z,{value:"3",children:"Alerting Settings"}),(0,a.jsx)(er.Z,{value:"4",children:"Email Alerts"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Active Logging Callbacks"}),(0,a.jsx)(x.Z,{numItems:2,children:(0,a.jsx)(L.Z,{className:"max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(K.Z,{children:"Callback Name"})})}),(0,a.jsx)(z.Z,{children:i.map((e,s)=>(0,a.jsxs)(W.Z,{className:"flex justify-between",children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.name})}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"flex justify-between",children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>{el(e),Q(!0)}}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,e.name),className:"ml-2",variant:"secondary",children:"Test Callback"})]})})]},s))})]})})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>F(!0),children:"Add Callback"})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(_.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,a.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{children:"Slack Webhook URL"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(et).map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:"region_outage_alerts"==s?n?(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)}):(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:s,type:"password",defaultValue:A&&A[s]?A[s]:N})})]},l)})})]}),(0,a.jsx)(p.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(et).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));console.log("key",s),console.log("webhookInput",n);let a=(null==n?void 0:n.value)||"";console.log("newWebhookValue",a),e[s]=a}),console.log("updatedAlertToWebhooks",e);let s={general_settings:{alert_to_webhook_url:e,alert_types:T}};console.log("payload",s);try{(0,u.K_)(l,s)}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"slack"),className:"mx-2",children:"Test Alerts"})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eK,{accessToken:l,premiumUser:n})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Email Settings"}),(0,a.jsxs)(_.Z,{children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: email alerts"})," ",(0,a.jsx)("br",{})]}),(0,a.jsx)("div",{className:"flex w-full",children:d.filter(e=>"email"===e.name).map((e,l)=>{var s;return(0,a.jsx)(B.Z,{children:(0,a.jsx)("ul",{children:(0,a.jsx)(x.Z,{numItems:2,children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).map(e=>{let[l,s]=e;return(0,a.jsxs)("li",{className:"mx-2 my-2",children:[!0!=n&&("EMAIL_LOGO_URL"===l||"EMAIL_SUPPORT_CONTACT"===l)?(0,a.jsxs)("div",{children:[(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,a.jsxs)(_.Z,{className:"mt-2",children:[" ","✨ ",l]})}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-2",children:l}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",style:{width:"400px"}})]}),(0,a.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===l&&(0,a.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},l)})})})},l)})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>ea(),children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"email"),className:"mx-2",children:"Test Email Alerts"})]})})]})]})}),(0,a.jsxs)(w.Z,{title:"Add Logging Callback",visible:O,width:800,onCancel:()=>F(!1),footer:null,children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: Logging"}),(0,a.jsx)(k.Z,{form:g,onFinish:eu,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(eW.Z,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,a.jsx)(v.default,{onChange:e=>{let l=M[e];l&&(console.log(l.ui_callback_name),eh(l))},children:M&&Object.values(M).map(e=>(0,a.jsx)(G.Z,{value:e.litellm_callback_name,children:e.ui_callback_name},e.litellm_callback_name))})}),Y&&Y.map(e=>(0,a.jsx)(eW.Z,{label:e,name:e,rules:[{required:!0,message:"Please enter the value for "+e}],children:(0,a.jsx)(j.Z,{type:"password"})},e)),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]}),(0,a.jsx)(w.Z,{visible:$,width:800,title:"Edit ".concat(null==ee?void 0:ee.name," Settings"),onCancel:()=>Q(!1),footer:null,children:(0,a.jsxs)(k.Z,{form:g,onFinish:em,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:ee&&ee.variables&&Object.entries(ee.variables).map(e=>{let[l,s]=e;return(0,a.jsx)(eW.Z,{label:l,name:l,children:(0,a.jsx)(j.Z,{type:"password",defaultValue:s})},l)})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:eY}=v.default;var eX=e=>{let{models:l,accessToken:s,routerSettings:t,setRouterSettings:n}=e,[i]=k.Z.useForm(),[o,d]=(0,r.useState)(!1),[c,m]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>d(!0),children:"+ Add Fallbacks"}),(0,a.jsx)(w.Z,{title:"Add Fallbacks",visible:o,width:800,footer:null,onOk:()=>{d(!1),i.resetFields()},onCancel:()=>{d(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:e=>{console.log(e);let{model_name:l,models:a}=e,r=[...t.fallbacks||[],{[l]:a}],o={...t,fallbacks:r};console.log(o);try{(0,u.K_)(s,{router_settings:o}),n(o)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully"),d(!1),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,a.jsx)(H.Z,{defaultValue:c,children:l&&l.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsx)(em.Z,{value:l,children:l&&l.filter(e=>e!=c).map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},e$=s(12968);async function eQ(e,l){console.log=function(){},console.log("isLocal:",!1);let s=window.location.origin,t=new e$.ZP.OpenAI({apiKey:l,baseURL:s,dangerouslyAllowBrowser:!0});try{let l=await t.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.ZP.success((0,a.jsxs)("span",{children:["Test model=",(0,a.jsx)("strong",{children:e}),", received model=",(0,a.jsx)("strong",{children:l.model}),". See"," ",(0,a.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}let e0={ttl:3600,lowest_latency_buffer:0},e1=e=>{let{selectedStrategy:l,strategyArgs:s,paramExplanation:t}=e;return(0,a.jsxs)(g.Z,{children:[(0,a.jsx)(f.Z,{className:"text-sm font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong",children:"Routing Strategy Specific Args"}),(0,a.jsx)(Z.Z,{children:"latency-based-routing"==l?(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(s).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:t[l]})]}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]})}):(0,a.jsx)(_.Z,{children:"No specific settings"})})]})};var e2=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)({}),[d,c]=(0,r.useState)({}),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(!1),[b]=k.Z.useForm(),[v,w]=(0,r.useState)(null),[N,I]=(0,r.useState)(null),[C,P]=(0,r.useState)(null),T={routing_strategy_args:"(dict) Arguments to pass to the routing strategy",routing_strategy:"(string) Routing strategy to use",allowed_fails:"(int) Number of times a deployment can fail before being added to cooldown",cooldown_time:"(int) time in seconds to cooldown a deployment after failure",num_retries:"(int) Number of retries for failed requests. Defaults to 0.",timeout:"(float) Timeout for requests. Defaults to None.",retry_after:"(int) Minimum time to wait before retrying a failed request",ttl:"(int) Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"(float) Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};(0,r.useEffect)(()=>{l&&s&&t&&((0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e);let l=e.router_settings;"model_group_retry_policy"in l&&delete l.model_group_retry_policy,o(l)}),(0,u.YU)(l).then(e=>{g(e)}))},[l,s,t]);let E=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(i.fallbacks)),i.fallbacks.map(l=>(e in l&&delete l[e],l));try{await (0,u.K_)(l,{router_settings:i}),o({...i}),I(i.routing_strategy),S.ZP.success("Router settings updated successfully")}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}}},O=(e,l)=>{g(m.map(s=>s.field_name===e?{...s,field_value:l}:s))},R=(e,s)=>{if(!l)return;let t=m[s].field_value;if(null!=t&&void 0!=t)try{(0,u.jA)(l,e,t);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:!0}:l);g(s)}catch(e){}},F=(e,s)=>{if(l)try{(0,u.ao)(l,e);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:null}:l);g(s)}catch(e){}},J=e=>{if(!l)return;console.log("router_settings",e);let s=Object.fromEntries(Object.entries(e).map(e=>{let[l,s]=e;if("routing_strategy_args"!==l&&"routing_strategy"!==l){var t;return[l,(null===(t=document.querySelector('input[name="'.concat(l,'"]')))||void 0===t?void 0:t.value)||s]}if("routing_strategy"==l)return[l,N];if("routing_strategy_args"==l&&"latency-based-routing"==N){let e={},l=document.querySelector('input[name="lowest_latency_buffer"]'),s=document.querySelector('input[name="ttl"]');return(null==l?void 0:l.value)&&(e.lowest_latency_buffer=Number(l.value)),(null==s?void 0:s.value)&&(e.ttl=Number(s.value)),console.log("setRoutingStrategyArgs: ".concat(e)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));console.log("updatedVariables",s);try{(0,u.K_)(l,{router_settings:s})}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully")};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Loadbalancing"}),(0,a.jsx)(er.Z,{value:"2",children:"Fallbacks"}),(0,a.jsx)(er.Z,{value:"3",children:"General"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,a.jsx)(y.Z,{children:"Router Settings"}),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(i).filter(e=>{let[l,s]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l&&"routing_strategy_args"!=l}).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:T[l]})]}),(0,a.jsx)(B.Z,{children:"routing_strategy"==l?(0,a.jsxs)(H.Z,{defaultValue:s,className:"w-full max-w-md",onValueChange:I,children:[(0,a.jsx)(G.Z,{value:"usage-based-routing",children:"usage-based-routing"}),(0,a.jsx)(G.Z,{value:"latency-based-routing",children:"latency-based-routing"}),(0,a.jsx)(G.Z,{value:"simple-shuffle",children:"simple-shuffle"})]}):(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]}),(0,a.jsx)(e1,{selectedStrategy:N,strategyArgs:i&&i.routing_strategy_args&&Object.keys(i.routing_strategy_args).length>0?i.routing_strategy_args:e0,paramExplanation:T})]}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>J(i),children:"Save Changes"})})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model Name"}),(0,a.jsx)(K.Z,{children:"Fallbacks"})]})}),(0,a.jsx)(z.Z,{children:i.fallbacks&&i.fallbacks.map((e,s)=>Object.entries(e).map(e=>{let[t,n]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:t}),(0,a.jsx)(B.Z,{children:Array.isArray(n)?n.join(", "):n}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>eQ(t,l),children:"Test Fallback"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>E(t)})})]},s.toString()+t)}))})]}),(0,a.jsx)(eX,{models:(null==n?void 0:n.data)?n.data.map(e=>e.model_name):[],accessToken:l,routerSettings:i,setRouterSettings:o})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"}),(0,a.jsx)(K.Z,{children:"Status"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:m.filter(e=>"TypedDictionary"!==e.field_type).map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),(0,a.jsx)(B.Z,{children:"Integer"==e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>O(e.field_name,l)}):null}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(p.Z,{onClick:()=>R(e.field_name,l),children:"Update"}),(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>F(e.field_name,l),children:"Reset"})]})]},l))})]})})})]})]})}):null},e4=s(98786),e5=s(74325),e8=e=>{let{value:l={},onChange:s}=e,[t,n]=(0,r.useState)(Object.entries(l)),i=e=>{let l=t.filter((l,s)=>s!==e);n(l),null==s||s(Object.fromEntries(l))},o=(e,l,a)=>{let r=[...t];r[e]=[l,a],n(r),null==s||s(Object.fromEntries(r))};return(0,a.jsxs)("div",{children:[t.map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(c.Z,{style:{display:"flex",marginBottom:8},align:"start",children:[(0,a.jsx)(j.Z,{placeholder:"Header Name",value:s,onChange:e=>o(l,e.target.value,t)}),(0,a.jsx)(j.Z,{placeholder:"Header Value",value:t,onChange:e=>o(l,s,e.target.value)}),(0,a.jsx)(e4.Z,{onClick:()=>i(l)})]},l)}),(0,a.jsx)(C.ZP,{type:"dashed",onClick:()=>{n([...t,["",""]])},icon:(0,a.jsx)(e5.Z,{}),children:"Add Header"})]})};let{Option:e3}=v.default;var e6=e=>{let{accessToken:l,setPassThroughItems:s,passThroughItems:t}=e,[n]=k.Z.useForm(),[i,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>o(!0),children:"+ Add Pass-Through Endpoint"}),(0,a.jsx)(w.Z,{title:"Add Pass-Through Endpoint",visible:i,width:800,footer:null,onOk:()=>{o(!1),n.resetFields()},onCancel:()=>{o(!1),n.resetFields()},children:(0,a.jsxs)(k.Z,{form:n,onFinish:e=>{console.log(e);let a=[...t,{headers:e.headers,path:e.path,target:e.target}];try{(0,u.Vt)(l,e),s(a)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("Pass through endpoint successfully added"),o(!1),n.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Path",name:"path",rules:[{required:!0,message:"The route to be added to the LiteLLM Proxy Server."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Target",name:"target",rules:[{required:!0,message:"The URL to which requests for this path should be forwarded."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Headers",name:"headers",rules:[{required:!0,message:"Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint"}],help:"required",children:(0,a.jsx)(e8,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Pass-Through Endpoint"})})]})})]})},e7=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&(0,u.mp)(l).then(e=>{o(e.endpoints)})},[l,s,t]);let d=(e,s)=>{if(l)try{(0,u.EG)(l,e);let s=i.filter(l=>l.path!==e);o(s),S.ZP.success("Endpoint deleted successfully.")}catch(e){}};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Path"}),(0,a.jsx)(K.Z,{children:"Target"}),(0,a.jsx)(K.Z,{children:"Headers"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:i.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.path})}),(0,a.jsx)(B.Z,{children:e.target}),(0,a.jsx)(B.Z,{children:JSON.stringify(e.headers)}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>d(e.path,l),children:"Reset"})})]},l))})]}),(0,a.jsx)(e6,{accessToken:l,setPassThroughItems:o,passThroughItems:i})]})})}):null},e9=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n}=e,[r]=k.Z.useForm(),i=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),r.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Create Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),r.resetFields()},onCancel:()=>{t(!1),r.resetFields()},children:(0,a.jsxs)(k.Z,{form:r,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Budget"})})]})})},le=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n,existingBudget:r}=e,[i]=k.Z.useForm(),o=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),i.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Edit Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),i.resetFields()},onCancel:()=>{t(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:r,children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Budget"})})]})})},ll=e=>{let{accessToken:l}=e,[s,t]=(0,r.useState)(!1),[n,i]=(0,r.useState)(!1),[o,d]=(0,r.useState)(null),[c,m]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&(0,u.O3)(l).then(e=>{m(e)})},[l]);let h=async(e,s)=>{null!=l&&(d(c[s]),i(!0))},x=async(e,s)=>{if(null==l)return;S.ZP.info("Request made"),await (0,u.NV)(l,e);let t=[...c];t.splice(s,1),m(t),S.ZP.success("Budget Deleted.")};return(0,a.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,a.jsx)(p.Z,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>t(!0),children:"+ Create Budget"}),(0,a.jsx)(e9,{accessToken:l,isModalVisible:s,setIsModalVisible:t,setBudgetList:m}),o&&(0,a.jsx)(le,{accessToken:l,isModalVisible:n,setIsModalVisible:i,setBudgetList:m,existingBudget:o}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"Create a budget to assign to customers."}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Budget ID"}),(0,a.jsx)(K.Z,{children:"Max Budget"}),(0,a.jsx)(K.Z,{children:"TPM"}),(0,a.jsx)(K.Z,{children:"RPM"})]})}),(0,a.jsx)(z.Z,{children:c.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.budget_id}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"n/a"}),(0,a.jsx)(B.Z,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,a.jsx)(B.Z,{children:e.rpm_limit?e.rpm_limit:"n/a"}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>h(e.budget_id,l)}),(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>x(e.budget_id,l)})]},l))})]})]}),(0,a.jsxs)("div",{className:"mt-5",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"How to use budget id"}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"Assign Budget to Customer"}),(0,a.jsx)(er.Z,{children:"Test it (Curl)"}),(0,a.jsx)(er.Z,{children:"Test it (OpenAI SDK)"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:"\ncurl -X POST --location '/end_user/new' \n-H 'Authorization: Bearer ' \n-H 'Content-Type: application/json' \n-d '{\"user_id\": \"my-customer-id', \"budget_id\": \"\"}' # \uD83D\uDC48 KEY CHANGE\n\n "})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:'\ncurl -X POST --location \'/chat/completions\' \n-H \'Authorization: Bearer \' \n-H \'Content-Type: application/json\' \n-d \'{\n "model": "gpt-3.5-turbo\', \n "messages":[{"role": "user", "content": "Hey, how\'s it going?"}],\n "user": "my-customer-id"\n}\' # \uD83D\uDC48 KEY CHANGE\n\n '})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'from openai import OpenAI\nclient = OpenAI(\n base_url="",\n api_key=""\n)\n\ncompletion = client.chat.completions.create(\n model="gpt-3.5-turbo",\n messages=[\n {"role": "system", "content": "You are a helpful assistant."},\n {"role": "user", "content": "Hello!"}\n ],\n user="my-customer-id"\n)\n\nprint(completion.choices[0].message)'})})]})]})]})]})},ls=s(41134),lt=e=>{let{proxySettings:l}=e,s="";return l&&l.PROXY_BASE_URL&&void 0!==l.PROXY_BASE_URL&&(s=l.PROXY_BASE_URL),(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,a.jsx)(_.Z,{className:"mt-2 mb-2",children:"LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below "}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"OpenAI Python SDK"}),(0,a.jsx)(er.Z,{children:"LlamaIndex"}),(0,a.jsx)(er.Z,{children:"Langchain Py"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="'.concat(s,'" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="'.concat(s,'", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="').concat(s,'",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="'.concat(s,'",\n model = "gpt-3.5-turbo",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})})};async function ln(e,l,s,t){console.log=function(){},console.log("isLocal:",!1);let n=window.location.origin,a=new e$.ZP.OpenAI({apiKey:t,baseURL:n,dangerouslyAllowBrowser:!0});try{for await(let t of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(t),t.choices[0].delta.content&&l(t.choices[0].delta.content)}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var la=e=>{let{accessToken:l,token:s,userRole:t,userID:n}=e,[i,o]=(0,r.useState)(""),[d,c]=(0,r.useState)(""),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(void 0),[y,b]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&n&&(async()=>{try{let e=await (0,u.So)(l,n,t);if(console.log("model_info:",e),(null==e?void 0:e.data.length)>0){let l=e.data.map(e=>({value:e.id,label:e.id}));if(console.log(l),l.length>0){let e=Array.from(new Set(l));console.log("Unique models:",e),e.sort((e,l)=>e.label.localeCompare(l.label)),console.log("Model info:",y),b(e)}f(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,n,t]);let k=(e,l)=>{g(s=>{let t=s[s.length-1];return t&&t.role===e?[...s.slice(0,s.length-1),{role:e,content:t.content+l}]:[...s,{role:e,content:l}]})},S=async()=>{if(""!==d.trim()&&i&&s&&t&&n){g(e=>[...e,{role:"user",content:d}]);try{Z&&await ln(d,e=>k("assistant",e),Z,i)}catch(e){console.error("Error fetching model response",e),k("assistant","Error fetching model response")}c("")}};if(t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,a.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsx)(eo.Z,{children:(0,a.jsx)(er.Z,{children:"Chat"})}),(0,a.jsx)(ec.Z,{children:(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("div",{className:"sm:max-w-2xl",children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"API Key"}),(0,a.jsx)(j.Z,{placeholder:"Type API Key here",type:"password",onValueChange:o,value:i})]}),(0,a.jsxs)(h.Z,{className:"mx-2",children:[(0,a.jsx)(_.Z,{children:"Select Model:"}),(0,a.jsx)(v.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),f(e)},options:y,style:{width:"200px"}})]})]})}),(0,a.jsxs)(V.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{})})}),(0,a.jsx)(z.Z,{children:m.map((e,l)=>(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,a.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(j.Z,{type:"text",value:d,onChange:e=>c(e.target.value),onKeyDown:e=>{"Enter"===e.key&&S()},placeholder:"Type your message..."}),(0,a.jsx)(p.Z,{onClick:S,className:"ml-2",children:"Send"})]})})]})})]})})})})},lr=s(33509),li=s(95781);let{Sider:lo}=lr.default,ld=["Admin","Admin Viewer","Internal User","Internal Viewer"];var lc=e=>{let{setPage:l,userRole:s,defaultSelectedKey:t}=e;return"Admin Viewer"==s?(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,a.jsx)(lo,{width:120,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["4"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9")]})})}):(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,a.jsx)(lo,{width:145,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["1"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("api-keys"),children:(0,a.jsx)(_.Z,{children:"Virtual Keys"})},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("llm-playground"),children:(0,a.jsx)(_.Z,{children:"Test Key"})},"3"),"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("models"),children:(0,a.jsx)(_.Z,{children:"Models"})},"2"):null,ld.includes(s)?(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:(0,a.jsx)(_.Z,{children:"Usage"})},"4"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("users"),children:(0,a.jsx)(_.Z,{children:"Internal Users"})},"5"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("settings"),children:(0,a.jsx)(_.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("budgets"),children:(0,a.jsx)(_.Z,{children:"Budgets"})},"10"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("general-settings"),children:(0,a.jsx)(_.Z,{children:"Router Settings"})},"11"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("pass-through-settings"),children:(0,a.jsx)(_.Z,{children:"Pass-Through"})},"12"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("admin-panel"),children:(0,a.jsx)(_.Z,{children:"Admin Settings"})},"13"):null,(0,a.jsx)(li.Z.Item,{onClick:()=>l("api_ref"),children:(0,a.jsx)(_.Z,{children:"API Reference"})},"14"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("model-hub"),children:(0,a.jsx)(_.Z,{children:"Model Hub"})},"16")]})})})},lm=s(67989),lu=s(52703);console.log("process.env.NODE_ENV","production"),console.log=function(){};let lh=e=>null!==e&&("Admin"===e||"Admin Viewer"===e);var lx=e=>{let{accessToken:l,token:s,userRole:t,userID:n,keys:i,premiumUser:o}=e,d=new Date,[c,m]=(0,r.useState)([]),[j,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)([]),[b,v]=(0,r.useState)([]),[k,S]=(0,r.useState)([]),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)([]),[C,P]=(0,r.useState)([]),[T,E]=(0,r.useState)([]),[O,R]=(0,r.useState)([]),[F,M]=(0,r.useState)({}),[D,U]=(0,r.useState)([]),[J,Y]=(0,r.useState)(""),[$,Q]=(0,r.useState)(["all-tags"]),[ee,el]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),es=new Date(d.getFullYear(),d.getMonth(),1),et=new Date(d.getFullYear(),d.getMonth()+1,0),ep=e_(es),ej=e_(et);function eg(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}console.log("keys in usage",i),console.log("premium user in usage",o),(0,r.useEffect)(()=>{ef(ee.from,ee.to)},[ee,$]);let eZ=async(e,s,t)=>{if(!e||!s||!l)return;s.setHours(23,59,59,999),e.setHours(0,0,0,0),console.log("uiSelectedKey",t);let n=await (0,u.b1)(l,t,e.toISOString(),s.toISOString());console.log("End user data updated successfully",n),v(n)},ef=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),N((await (0,u.J$)(l,e.toISOString(),s.toISOString(),0===$.length?void 0:$)).spend_per_tag),console.log("Tag spend data updated successfully"))};function e_(e){let l=e.getFullYear(),s=e.getMonth()+1,t=e.getDate();return"".concat(l,"-").concat(s<10?"0"+s:s,"-").concat(t<10?"0"+t:t)}console.log("Start date is ".concat(ep)),console.log("End date is ".concat(ej));let ey=async(e,l,s)=>{try{let s=await e();l(s)}catch(e){console.error(s,e)}},eb=()=>ey(()=>l?(0,u.FC)(l):Promise.reject("No access token"),m,"Error fetching overall spend"),ev=()=>ey(()=>l&&s?(0,u.OU)(l,s,ep,ej):Promise.reject("No access token or token"),R,"Error fetching provider spend"),ek=async()=>{l&&await ey(async()=>(await (0,u.tN)(l)).map(e=>({key:(e.key_alias||e.key_name||e.api_key).substring(0,10),spend:e.total_spend})),g,"Error fetching top keys")},eS=async()=>{l&&await ey(async()=>(await (0,u.Au)(l)).map(e=>({key:e.model,spend:e.total_spend})),f,"Error fetching top models")},ew=async()=>{l&&await ey(async()=>{let e=await (0,u.mR)(l);return S(e.daily_spend),P(e.teams),e.total_spend_per_team.map(e=>({name:e.team_id||"",value:(e.total_spend||0).toFixed(2)}))},E,"Error fetching team spend")},eN=()=>{l&&ey(async()=>(await (0,u.X)(l)).tag_names,A,"Error fetching tag names")},eI=()=>{l&&ey(()=>{var e,s;return(0,u.J$)(l,null===(e=ee.from)||void 0===e?void 0:e.toISOString(),null===(s=ee.to)||void 0===s?void 0:s.toISOString(),void 0)},e=>N(e.spend_per_tag),"Error fetching top tags")},eA=()=>{l&&ey(()=>(0,u.b1)(l,null,void 0,void 0),v,"Error fetching top end users")},eC=()=>{l&&ey(()=>(0,u.wd)(l,ep,ej),M,"Error fetching global activity")},eP=()=>{l&&ey(()=>(0,u.xA)(l,ep,ej),U,"Error fetching global activity per model")};return(0,r.useEffect)(()=>{l&&s&&t&&n&&(eb(),ev(),ek(),eS(),eC(),eP(),lh(t)&&(ew(),eN(),eI(),eA()))},[l,s,t,n,ep,ej]),(0,a.jsx)("div",{style:{width:"100%"},className:"p-8",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{className:"mt-2",children:[(0,a.jsx)(er.Z,{children:"All Up"}),lh(t)?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(er.Z,{children:"Team Based Usage"}),(0,a.jsx)(er.Z,{children:"Customer Usage"}),(0,a.jsx)(er.Z,{children:"Tag Based Usage"})]}):(0,a.jsx)(a.Fragment,{children:(0,a.jsx)("div",{})})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"solid",className:"mt-1",children:[(0,a.jsx)(er.Z,{children:"Cost"}),(0,a.jsx)(er.Z,{children:"Activity"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[100vh] w-full",children:[(0,a.jsx)(X,{userID:n,userRole:t,accessToken:l,userSpend:null,selectedTeam:null,userMaxBudget:null}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Monthly Spend"}),(0,a.jsx)(ex.Z,{data:c,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top API Keys"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:j,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top Models"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:Z,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"✨ Spend by Provider"}),o?(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(lu.Z,{className:"mt-4 h-40",variant:"pie",data:O,index:"provider",category:"spend"})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Provider"}),(0,a.jsx)(K.Z,{children:"Spend"})]})}),(0,a.jsx)(z.Z,{children:O.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.provider}),(0,a.jsx)(B.Z,{children:1e-5>parseFloat(e.spend.toFixed(2))?"less than 0.00":e.spend.toFixed(2)})]},e.provider))})]})})]})}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})]})]})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"All Up"}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(F.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["api_requests"],onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(F.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]}),o?(0,a.jsx)(a.Fragment,{children:D.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["total_tokens"],valueFormatter:eg,onValueChange:e=>console.log(e)})]})]})]},l))}):(0,a.jsx)(a.Fragment,{children:D&&D.length>0&&D.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Activity by Model"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see analytics for all models"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],valueFormatter:eg,categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]})]},l))})]})})]})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(h.Z,{numColSpan:2,children:[(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"Total Spend Per Team"}),(0,a.jsx)(lm.Z,{data:T})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Daily Spend Per Team"}),(0,a.jsx)(ex.Z,{className:"h-72",data:k,showLegend:!0,index:"date",categories:C,yAxisWidth:80,stack:!0})]})]}),(0,a.jsx)(h.Z,{numColSpan:2})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:["Customers of your LLM API calls. Tracked when a `user` param is passed in your LLM calls ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/users",target:"_blank",children:"docs here"})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:ee,onValueChange:e=>{el(e),eZ(e.from,e.to,null)}})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Key"}),(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{eZ(ee.from,ee.to,null)},children:"All Keys"},"all-keys"),null==i?void 0:i.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{eZ(ee.from,ee.to,e.token)},children:e.key_alias},l):null)]})]})]}),(0,a.jsx)(L.Z,{className:"mt-4",children:(0,a.jsxs)(V.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Customer"}),(0,a.jsx)(K.Z,{children:"Spend"}),(0,a.jsx)(K.Z,{children:"Total Events"})]})}),(0,a.jsx)(z.Z,{children:null==b?void 0:b.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.end_user}),(0,a.jsx)(B.Z,{children:null===(s=e.total_spend)||void 0===s?void 0:s.toFixed(4)}),(0,a.jsx)(B.Z,{children:e.total_count})]},l)})})]})})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(ea.Z,{className:"mb-4",enableSelect:!0,value:ee,onValueChange:e=>{el(e),ef(e.from,e.to)}})}),(0,a.jsx)(h.Z,{children:o?(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsx)(eu.Z,{value:String(e),children:e},e))]})}):(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsxs)(G.Z,{value:String(e),disabled:!0,children:["✨ ",e," (Enterprise only Feature)"]},e))]})})})]}),(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Spend Per Tag"}),(0,a.jsxs)(_.Z,{children:["Get Started Tracking cost per tag ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/cost_tracking",target:"_blank",children:"here"})]}),(0,a.jsx)(ex.Z,{className:"h-72",data:w,index:"name",categories:["spend"],colors:["blue"]})]})}),(0,a.jsx)(h.Z,{numColSpan:2})]})]})]})]})})};let lp=e=>{if(e)return e.toISOString().split("T")[0]};function lj(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}var lg=e=>{let{accessToken:l,token:s,userRole:t,userID:n,premiumUser:i}=e,[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[p,j]=(0,r.useState)([]),[g,Z]=(0,r.useState)([]),[f,_]=(0,r.useState)("0"),[y,b]=(0,r.useState)("0"),[v,k]=(0,r.useState)("0"),[S,w]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date});(0,r.useEffect)(()=>{l&&S&&(async()=>{Z(await (0,u.zg)(l,lp(S.from),lp(S.to)))})()},[l]);let N=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.api_key)&&void 0!==l?l:""}))),I=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.model)&&void 0!==l?l:""})));Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.call_type)&&void 0!==l?l:""})));let A=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),Z(await (0,u.zg)(l,lp(e),lp(s))))};return(0,r.useEffect)(()=>{console.log("DATA IN CACHE DASHBOARD",g);let e=g;c.length>0&&(e=e.filter(e=>c.includes(e.api_key))),p.length>0&&(e=e.filter(e=>p.includes(e.model))),console.log("before processed data in cache dashboard",e);let l=0,s=0,t=0,n=e.reduce((e,n)=>{console.log("Processing item:",n),n.call_type||(console.log("Item has no call_type:",n),n.call_type="Unknown"),l+=(n.total_rows||0)-(n.cache_hit_true_rows||0),s+=n.cache_hit_true_rows||0,t+=n.cached_completion_tokens||0;let a=e.find(e=>e.name===n.call_type);return a?(a["LLM API requests"]+=(n.total_rows||0)-(n.cache_hit_true_rows||0),a["Cache hit"]+=n.cache_hit_true_rows||0,a["Cached Completion Tokens"]+=n.cached_completion_tokens||0,a["Generated Completion Tokens"]+=n.generated_completion_tokens||0):e.push({name:n.call_type,"LLM API requests":(n.total_rows||0)-(n.cache_hit_true_rows||0),"Cache hit":n.cache_hit_true_rows||0,"Cached Completion Tokens":n.cached_completion_tokens||0,"Generated Completion Tokens":n.generated_completion_tokens||0}),e},[]);_(lj(s)),b(lj(t));let a=s+l;a>0?k((s/a*100).toFixed(2)):k("0"),d(n),console.log("PROCESSED DATA IN CACHE DASHBOARD",n)},[c,p,S,g]),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(x.Z,{numItems:3,className:"gap-4 mt-4",children:[(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select API Keys",value:c,onValueChange:m,children:N.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select Models",value:p,onValueChange:j,children:I.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(ea.Z,{enableSelect:!0,value:S,onValueChange:e=>{w(e),A(e.from,e.to)},selectPlaceholder:"Select date range"})})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-4",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hit Ratio"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsxs)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:[v,"%"]})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hits"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:f})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cached Tokens"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:y})})]})]}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cache Hits vs API Requests"}),(0,a.jsx)(ex.Z,{title:"Cache Hits vs API Requests",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["LLM API requests","Cache hit"],colors:["sky","teal"],yAxisWidth:48}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cached Completion Tokens vs Generated Completion Tokens"}),(0,a.jsx)(ex.Z,{className:"mt-6",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["Generated Completion Tokens","Cached Completion Tokens"],colors:["sky","teal"],yAxisWidth:48})]})},lZ=()=>{let{Title:e,Paragraph:l}=es.default,[s,t]=(0,r.useState)(""),[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)(null),[h,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(null),[g,Z]=(0,r.useState)({PROXY_BASE_URL:"",PROXY_LOGOUT_URL:""}),[f,_]=(0,r.useState)(!0),y=(0,i.useSearchParams)(),[b,v]=(0,r.useState)({data:[]}),k=y.get("userID"),S=y.get("invitation_id"),w=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),[N,I]=(0,r.useState)("api-keys"),[A,C]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(w){let e=(0,el.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),C(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"internal_user":return"Internal User";case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),t(l),"Admin Viewer"==l&&I("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?_("username_password"==e.login_method):console.log("User Email is not set ".concat(e)),e.premium_user&&o(e.premium_user),e.auth_header_name&&(0,u.K8)(e.auth_header_name)}}},[w]),(0,a.jsx)(r.Suspense,{fallback:(0,a.jsx)("div",{children:"Loading..."}),children:S?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):(0,a.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,a.jsx)(m,{userID:k,userRole:s,userEmail:d,premiumUser:n,setProxySettings:Z,proxySettings:g}),(0,a.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,a.jsx)("div",{className:"mt-8",children:(0,a.jsx)(lc,{setPage:I,userRole:s,defaultSelectedKey:null})}),"api-keys"==N?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):"models"==N?(0,a.jsx)(eO,{userID:k,userRole:s,token:w,keys:p,accessToken:A,modelData:b,setModelData:v,premiumUser:n}):"llm-playground"==N?(0,a.jsx)(la,{userID:k,userRole:s,token:w,accessToken:A}):"users"==N?(0,a.jsx)(eL,{userID:k,userRole:s,token:w,keys:p,teams:h,accessToken:A,setKeys:j}):"teams"==N?(0,a.jsx)(eU,{teams:h,setTeams:x,searchParams:y,accessToken:A,userID:k,userRole:s}):"admin-panel"==N?(0,a.jsx)(eV,{setTeams:x,searchParams:y,accessToken:A,showSSOBanner:f,premiumUser:n}):"api_ref"==N?(0,a.jsx)(lt,{proxySettings:g}):"settings"==N?(0,a.jsx)(eJ,{userID:k,userRole:s,accessToken:A,premiumUser:n}):"budgets"==N?(0,a.jsx)(ll,{accessToken:A}):"general-settings"==N?(0,a.jsx)(e2,{userID:k,userRole:s,accessToken:A,modelData:b}):"model-hub"==N?(0,a.jsx)(ls.Z,{accessToken:A,publicPage:!1,premiumUser:n}):"caching"==N?(0,a.jsx)(lg,{userID:k,userRole:s,token:w,accessToken:A,premiumUser:n}):"pass-through-settings"==N?(0,a.jsx)(e7,{userID:k,userRole:s,accessToken:A,modelData:b}):(0,a.jsx)(lx,{userID:k,userRole:s,token:w,accessToken:A,keys:p,premiumUser:n})]})]})})}},41134:function(e,l,s){"use strict";s.d(l,{Z:function(){return y}});var t=s(57437),n=s(2265),a=s(47907),r=s(777),i=s(2179),o=s(13810),d=s(92836),c=s(26734),m=s(41608),u=s(32126),h=s(23682),x=s(71801),p=s(42440),j=s(84174),g=s(50459),Z=s(6180),f=s(99129),_=s(67951),y=e=>{var l;let{accessToken:s,publicPage:y,premiumUser:b}=e,[v,k]=(0,n.useState)(!1),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(!1),[A,C]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),E=(0,a.useRouter)();(0,n.useEffect)(()=>{s&&(async()=>{try{let e=await (0,r.kn)(s);console.log("ModelHubData:",e),w(e.data),(0,r.E9)(s,"enable_public_model_hub").then(e=>{console.log("data: ".concat(JSON.stringify(e))),!0==e.field_value&&k(!0)}).catch(e=>{})}catch(e){console.error("There was an error fetching the model data",e)}})()},[s,y]);let O=e=>{T(e),I(!0)},R=async()=>{s&&(0,r.jA)(s,"enable_public_model_hub",!0).then(e=>{C(!0)})},F=()=>{I(!1),C(!1),T(null)},M=()=>{I(!1),C(!1),T(null)},D=e=>{navigator.clipboard.writeText(e)};return(0,t.jsxs)("div",{children:[y&&v||!1==y?(0,t.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,t.jsx)("div",{className:"relative w-full"}),(0,t.jsxs)("div",{className:"flex ".concat(y?"justify-between":"items-center"),children:[(0,t.jsx)(p.Z,{className:"ml-8 text-center ",children:"Model Hub"}),!1==y?b?(0,t.jsx)(i.Z,{className:"ml-4",onClick:()=>R(),children:"✨ Make Public"}):(0,t.jsx)(i.Z,{className:"ml-4",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Make Public"})}):(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)("p",{children:"Filter by key:"}),(0,t.jsx)(x.Z,{className:"bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center",children:"/ui/model_hub?key="})]})]}),(0,t.jsx)("div",{className:"grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4 pr-8",children:S&&S.map(e=>(0,t.jsxs)(o.Z,{className:"mt-5 mx-8",children:[(0,t.jsxs)("pre",{className:"flex justify-between",children:[(0,t.jsx)(p.Z,{children:e.model_group}),(0,t.jsx)(Z.Z,{title:e.model_group,children:(0,t.jsx)(j.Z,{onClick:()=>D(e.model_group),style:{cursor:"pointer",marginRight:"10px"}})})]}),(0,t.jsxs)("div",{className:"my-5",children:[(0,t.jsxs)(x.Z,{children:["Mode: ",e.mode]}),(0,t.jsxs)(x.Z,{children:["Supports Function Calling:"," ",(null==e?void 0:e.supports_function_calling)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Supports Vision:"," ",(null==e?void 0:e.supports_vision)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Max Input Tokens:"," ",(null==e?void 0:e.max_input_tokens)?null==e?void 0:e.max_input_tokens:"N/A"]}),(0,t.jsxs)(x.Z,{children:["Max Output Tokens:"," ",(null==e?void 0:e.max_output_tokens)?null==e?void 0:e.max_output_tokens:"N/A"]})]}),(0,t.jsx)("div",{style:{marginTop:"auto",textAlign:"right"},children:(0,t.jsxs)("a",{href:"#",onClick:()=>O(e),style:{color:"#1890ff",fontSize:"smaller"},children:["View more ",(0,t.jsx)(g.Z,{})]})})]},e.model_group))})]}):(0,t.jsxs)(o.Z,{className:"mx-auto max-w-xl mt-10",children:[(0,t.jsx)(x.Z,{className:"text-xl text-center mb-2 text-black",children:"Public Model Hub not enabled."}),(0,t.jsx)("p",{className:"text-base text-center text-slate-800",children:"Ask your proxy admin to enable this on their Admin UI."})]}),(0,t.jsx)(f.Z,{title:"Public Model Hub",width:600,visible:A,footer:null,onOk:F,onCancel:M,children:(0,t.jsxs)("div",{className:"pt-5 pb-5",children:[(0,t.jsxs)("div",{className:"flex justify-between mb-4",children:[(0,t.jsx)(x.Z,{className:"text-base mr-2",children:"Shareable Link:"}),(0,t.jsx)(x.Z,{className:"max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded",children:"/ui/model_hub?key="})]}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(i.Z,{onClick:()=>{E.replace("/model_hub?key=".concat(s))},children:"See Page"})})]})}),(0,t.jsx)(f.Z,{title:P&&P.model_group?P.model_group:"Unknown Model",width:800,visible:N,footer:null,onOk:F,onCancel:M,children:P&&(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-4",children:(0,t.jsx)("strong",{children:"Model Information & Usage"})}),(0,t.jsxs)(c.Z,{children:[(0,t.jsxs)(m.Z,{children:[(0,t.jsx)(d.Z,{children:"OpenAI Python SDK"}),(0,t.jsx)(d.Z,{children:"Supported OpenAI Params"}),(0,t.jsx)(d.Z,{children:"LlamaIndex"}),(0,t.jsx)(d.Z,{children:"Langchain Py"})]}),(0,t.jsxs)(h.Z,{children:[(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="'.concat(P.model_group,'", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:"".concat(null===(l=P.supported_openai_params)||void 0===l?void 0:l.map(e=>"".concat(e,"\n")).join(""))})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="'.concat(P.model_group,'", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:4000",\n model = "'.concat(P.model_group,'",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})]})}}},function(e){e.O(0,[665,936,902,131,684,626,777,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/index.html b/litellm/proxy/_experimental/out/index.html index 2f7b4e28c7..c482cdd956 100644 --- a/litellm/proxy/_experimental/out/index.html +++ b/litellm/proxy/_experimental/out/index.html @@ -1 +1 @@ -LiteLLM Dashboard \ No newline at end of file +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/index.txt b/litellm/proxy/_experimental/out/index.txt index 296e848453..7bd7e345de 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[68031,["665","static/chunks/3014691f-b24e8254c7593934.js","936","static/chunks/2f6dbc85-cac2949a76539886.js","902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","684","static/chunks/684-16b194c83a169f6d.js","626","static/chunks/626-fc3969bfc35ead00.js","777","static/chunks/777-a81b45dec53652df.js","931","static/chunks/app/page-7c218fb97a2a9817.js"],""] +3:I[68031,["665","static/chunks/3014691f-b24e8254c7593934.js","936","static/chunks/2f6dbc85-cac2949a76539886.js","902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","684","static/chunks/684-16b194c83a169f6d.js","626","static/chunks/626-fc3969bfc35ead00.js","777","static/chunks/777-a81b45dec53652df.js","931","static/chunks/app/page-7b75dc53f1c6e449.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/litellm/proxy/_experimental/out/model_hub.html b/litellm/proxy/_experimental/out/model_hub.html new file mode 100644 index 0000000000..bbf4caf53e --- /dev/null +++ b/litellm/proxy/_experimental/out/model_hub.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/model_hub.txt b/litellm/proxy/_experimental/out/model_hub.txt index 6de8880d9b..e95d7b6b45 100644 --- a/litellm/proxy/_experimental/out/model_hub.txt +++ b/litellm/proxy/_experimental/out/model_hub.txt @@ -2,6 +2,6 @@ 3:I[87494,["902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","777","static/chunks/777-a81b45dec53652df.js","418","static/chunks/app/model_hub/page-8ed460f3f33c0bf2.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/litellm/proxy/_experimental/out/onboarding.html b/litellm/proxy/_experimental/out/onboarding.html new file mode 100644 index 0000000000..82b9b495fd --- /dev/null +++ b/litellm/proxy/_experimental/out/onboarding.html @@ -0,0 +1 @@ +LiteLLM Dashboard \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/onboarding.txt b/litellm/proxy/_experimental/out/onboarding.txt index 38e7f4b555..fc2ff8abeb 100644 --- a/litellm/proxy/_experimental/out/onboarding.txt +++ b/litellm/proxy/_experimental/out/onboarding.txt @@ -2,6 +2,6 @@ 3:I[667,["665","static/chunks/3014691f-b24e8254c7593934.js","902","static/chunks/902-58bf23027703b2e8.js","684","static/chunks/684-16b194c83a169f6d.js","777","static/chunks/777-a81b45dec53652df.js","461","static/chunks/app/onboarding/page-cba59362096ed469.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/ui/litellm-dashboard/out/404.html b/ui/litellm-dashboard/out/404.html index 3387c26cee..40924db8de 100644 --- a/ui/litellm-dashboard/out/404.html +++ b/ui/litellm-dashboard/out/404.html @@ -1 +1 @@ -404: This page could not be found.LiteLLM Dashboard

404

This page could not be found.

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

404

This page could not be found.

\ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js new file mode 100644 index 0000000000..4eeeca15dd --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-7b75dc53f1c6e449.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,s){Promise.resolve().then(s.bind(s,68031))},667:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return f}});var t=s(57437),n=s(2265),a=s(47907),r=s(2179),i=s(18190),o=s(13810),d=s(10384),c=s(46453),m=s(71801),u=s(52273),h=s(42440),x=s(30953),p=s(777),j=s(37963),g=s(60620),Z=s(13565);function f(){let[e]=g.Z.useForm(),l=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));l&&l.split("=")[1]}("token");let s=l.get("invitation_id"),[f,_]=(0,n.useState)(null),[y,b]=(0,n.useState)(""),[v,k]=(0,n.useState)(""),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(""),[A,C]=(0,n.useState)("");return(0,n.useEffect)(()=>{s&&(0,p.W_)(s).then(e=>{let l=e.login_url;console.log("login_url:",l),I(l);let s=e.token,t=(0,j.o)(s);C(s),console.log("decoded:",t),_(t.key),console.log("decoded user email:",t.user_email),k(t.user_email),w(t.user_id)})},[s]),(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,t.jsxs)(o.Z,{children:[(0,t.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,t.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,t.jsx)(m.Z,{children:"Claim your user account to login to Admin UI."}),(0,t.jsx)(i.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,t.jsxs)(c.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,t.jsx)(d.Z,{children:"SSO is under the Enterprise Tirer."}),(0,t.jsx)(d.Z,{children:(0,t.jsx)(r.Z,{variant:"primary",className:"mb-2",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,t.jsxs)(g.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",f,"token:",A,"formValues:",e),f&&A&&(e.user_email=v,S&&s&&(0,p.m_)(f,s,S,e.password).then(e=>{var l;let s="/ui/";s+="?userID="+((null===(l=e.data)||void 0===l?void 0:l.user_id)||e.user_id),document.cookie="token="+A,console.log("redirecting to:",s),window.location.href=s}))},children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.Z.Item,{label:"Email Address",name:"user_email",children:(0,t.jsx)(u.Z,{type:"email",disabled:!0,value:v,defaultValue:v,className:"max-w-md"})}),(0,t.jsx)(g.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,t.jsx)(u.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,t.jsx)("div",{className:"mt-10",children:(0,t.jsx)(Z.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}},68031:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return lZ}});var t,n,a=s(57437),r=s(2265),i=s(47907),o=s(8792),d=s(40491),c=s(65270),m=e=>{let{userID:l,userRole:s,userEmail:t,premiumUser:n,setProxySettings:r,proxySettings:i}=e;console.log("User ID:",l),console.log("userEmail:",t),console.log("premiumUser:",n),console.log=function(){};let m="";console.log("PROXY_settings=",i),i&&i.PROXY_LOGOUT_URL&&void 0!==i.PROXY_LOGOUT_URL&&(m=i.PROXY_LOGOUT_URL),console.log("logoutUrl=",m);let u=[{key:"1",label:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{children:["Role: ",s]}),(0,a.jsxs)("p",{children:["ID: ",l]}),(0,a.jsxs)("p",{children:["Premium User: ",String(n)]})]})},{key:"2",label:(0,a.jsx)("p",{onClick:()=>{document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=m},children:"Logout"})}];return(0,a.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,a.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,a.jsx)("div",{className:"flex flex-col items-center",children:(0,a.jsx)(o.default,{href:"/",children:(0,a.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,a.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,a.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[n?null:(0,a.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Get enterprise license"})}),(0,a.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,a.jsx)(d.Z,{menu:{items:u},children:(0,a.jsx)(c.Z,{children:t||s})})})]})]})},u=s(777),h=s(10384),x=s(46453),p=s(2179),j=s(52273),g=s(26780),Z=s(15595),f=s(6698),_=s(71801),y=s(42440),b=s(42308),v=s(50670),k=s(60620),S=s(80588),w=s(99129),N=s(18559),I=s(44839),A=s(88707),C=s(13565);let{Option:P}=v.default;var T=e=>{let{userID:l,team:s,userRole:t,accessToken:n,data:i,setData:o}=e,[d]=k.Z.useForm(),[c,m]=(0,r.useState)(!1),[T,E]=(0,r.useState)(null),[O,R]=(0,r.useState)(null),[F,M]=(0,r.useState)([]),[D,L]=(0,r.useState)([]),[U,V]=(0,r.useState)("you"),z=()=>{m(!1),d.resetFields()},B=()=>{m(!1),E(null),d.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==n){let e=(await (0,u.So)(n,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),M(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,t]);let q=async e=>{try{var s,t,a;let r=null!==(s=null==e?void 0:e.key_alias)&&void 0!==s?s:"",c=null!==(t=null==e?void 0:e.team_id)&&void 0!==t?t:null;if((null!==(a=null==i?void 0:i.filter(e=>e.team_id===c).map(e=>e.key_alias))&&void 0!==a?a:[]).includes(r))throw Error("Key alias ".concat(r," already exists for team with ID ").concat(c,", please provide another key alias"));if(S.ZP.info("Making API Call"),m(!0),"service_account"===U){let l={};try{l=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}l.service_account_id=e.key_alias,e.metadata=JSON.stringify(l)}let h=await (0,u.wX)(n,l,e);console.log("key create Response:",h),o(e=>e?[...e,h]:[h]),E(h.key),R(h.soft_budget),S.ZP.success("API Key Created"),d.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,r.useEffect)(()=>{L(s&&s.models.length>0?s.models.includes("all-proxy-models")?F:s.models:F)},[s,F]),(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,a.jsx)(w.Z,{title:"Create Key",visible:c,width:800,footer:null,onOk:z,onCancel:B,children:(0,a.jsxs)(k.Z,{form:d,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Owned By",className:"mb-4",children:(0,a.jsxs)(N.ZP.Group,{onChange:e=>V(e.target.value),value:U,children:[(0,a.jsx)(N.ZP,{value:"you",children:"You"}),(0,a.jsx)(N.ZP,{value:"service_account",children:"Service Account"})]})}),(0,a.jsx)(k.Z.Item,{label:"you"===U?"Key Name":"Service Account ID",name:"key_alias",rules:[{required:!0,message:"Please input a ".concat("you"===U?"key name":"service account ID")}],help:"you"===U?"required":"IDs can include letters, numbers, and hyphens",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:s?s.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&d.setFieldsValue({models:["all-team-models"]})},children:[(0,a.jsx)(P,{value:"all-team-models",children:"All Team Models"},"all-team-models"),D.map(e=>(0,a.jsx)(P,{value:e,children:e},e))]})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==s?void 0:s.max_budget)!==null&&(null==s?void 0:s.max_budget)!==void 0?null==s?void 0:s.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.max_budget&&l>s.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(s.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==s?void 0:s.budget_duration)!==null&&(null==s?void 0:s.budget_duration)!==void 0?null==s?void 0:s.budget_duration:"None"),children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==s?void 0:s.tpm_limit)!==null&&(null==s?void 0:s.tpm_limit)!==void 0?null==s?void 0:s.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.tpm_limit&&l>s.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(s.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==s?void 0:s.rpm_limit)!==null&&(null==s?void 0:s.rpm_limit)!==void 0?null==s?void 0:s.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.rpm_limit&&l>s.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(s.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",className:"mt-8",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Key"})})]})}),T&&(0,a.jsx)(w.Z,{visible:c,onOk:z,onCancel:B,footer:null,children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Save your Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:null!=T?(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:T})}),(0,a.jsx)(b.CopyToClipboard,{text:T,onCopy:()=>{S.ZP.success("API Key copied to clipboard")},children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,a.jsx)(_.Z,{children:"Key being created, this might take 30s"})})]})})]})},E=s(66002),O=s(9454),R=s(98941),F=s(63954),M=s(33393),D=s(5),L=s(13810),U=s(61244),V=s(10827),z=s(3851),B=s(2044),q=s(64167),K=s(74480),W=s(7178),H=s(95093),G=s(27166);let{Option:J}=v.default;console.log=function(){};var Y=e=>{let{userID:l,userRole:s,accessToken:t,selectedTeam:n,data:i,setData:o,teams:d,premiumUser:c}=e,[m,g]=(0,r.useState)(!1),[Z,f]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[P,T]=(0,r.useState)(null),[Y,X]=(0,r.useState)(null),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[es,et]=(0,r.useState)(!1),[en,ea]=(0,r.useState)(null),[er,ei]=(0,r.useState)([]),eo=new Set,[ed,ec]=(0,r.useState)(!1),[em,eu]=(0,r.useState)(!1),[eh,ex]=(0,r.useState)(null),[ep,ej]=(0,r.useState)(null),[eg]=k.Z.useForm(),[eZ,ef]=(0,r.useState)(null),[e_,ey]=(0,r.useState)(eo);(0,r.useEffect)(()=>{console.log("in calculateNewExpiryTime for selectedToken",en),(null==ep?void 0:ep.duration)?ef((e=>{if(!e)return null;try{let l;let s=new Date;if(e.endsWith("s"))l=(0,E.Z)(s,{seconds:parseInt(e)});else if(e.endsWith("h"))l=(0,E.Z)(s,{hours:parseInt(e)});else if(e.endsWith("d"))l=(0,E.Z)(s,{days:parseInt(e)});else throw Error("Invalid duration format");return l.toLocaleString("en-US",{year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",hour12:!0})}catch(e){return null}})(ep.duration)):ef(null),console.log("calculateNewExpiryTime:",eZ)},[en,null==ep?void 0:ep.duration]),(0,r.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==t&&null!==s){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),ei(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[t,l,s]);let eb=e=>{ea(e),ec(!0)},ev=async e=>{if(null==t||null==en)return;let l={...en,metadata:e,key:en.token};try{let e=await (0,u.Nc)(t,l);if(console.log("Model limits updated:",e),i){let l=i.map(l=>l.token===en.token?e:l);o(l)}S.ZP.success("Model-specific limits updated successfully")}catch(e){console.error("Error updating model-specific limits:",e),S.ZP.error("Failed to update model-specific limits")}ec(!1),ea(null)};(0,r.useEffect)(()=>{if(d){let e=new Set;d.forEach((l,s)=>{let t=l.team_id;e.add(t)}),ey(e)}},[d]);let ek=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id);let l=null;if(e.budget_duration)switch(e.budget_duration){case"24h":l="daily";break;case"7d":l="weekly";break;case"30d":l="monthly";break;default:l="None"}ea({...e,budget_duration:l}),el(!0)},eS=async e=>{if(null==t)return;let l=e.token;if(e.key=l,e.budget_duration)switch(e.budget_duration){case"daily":e.budget_duration="24h";break;case"weekly":e.budget_duration="7d";break;case"monthly":e.budget_duration="30d"}console.log("handleEditSubmit:",e);let s=await (0,u.Nc)(t,e);console.log("handleEditSubmit: newKeyValues",s),i&&o(i.map(e=>e.token===l?s:e)),S.ZP.success("Key updated successfully"),el(!1),ea(null)},ew=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=i&&(I(e.token),localStorage.removeItem("userData"+l),f(!0))},eN=async()=>{if(null!=N&&null!=i){try{await (0,u.I1)(t,N);let e=i.filter(e=>e.token!==N);o(e)}catch(e){console.error("Error deleting the key:",e)}f(!1),I(null)}},eI=e=>{ea(e),ef(null),eg.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||""}),eu(!0)},eA=(e,l)=>{ej(s=>({...s,[e]:l}))},eC=async()=>{if(!c){S.ZP.error("Regenerate API Key is an Enterprise feature. Please upgrade to use this feature.");return}if(null!=en)try{let e=await eg.validateFields(),l=await (0,u.s0)(t,en.token,e);if(ex(l.key),i){let s=i.map(s=>s.token===(null==en?void 0:en.token)?{...s,key_name:l.key_name,...e}:s);o(s)}eu(!1),eg.resetFields(),S.ZP.success("API Key regenerated successfully")}catch(e){console.error("Error regenerating key:",e),S.ZP.error("Failed to regenerate API Key")}};if(null!=i)return console.log("RERENDER TRIGGERED"),(0,a.jsxs)("div",{children:[(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,a.jsxs)(V.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Key Alias"}),(0,a.jsx)(K.Z,{children:"Secret Key"}),(0,a.jsx)(K.Z,{children:"Created"}),(0,a.jsx)(K.Z,{children:"Expires"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Budget Reset"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"Rate Limits"}),(0,a.jsx)(K.Z,{children:"Rate Limits per model"})]})}),(0,a.jsx)(z.Z,{children:i.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(n){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(e_.has(e.team_id),", selectedTeam id: ").concat(n.team_id)),(null!=n.team_id||null===e.team_id||e_.has(e.team_id))&&e.team_id!=n.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,a.jsx)(_.Z,{children:e.key_alias}):(0,a.jsx)(_.Z,{children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.key_name})}),(0,a.jsx)(B.Z,{children:null!=e.created_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.created_at).toLocaleDateString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Not available"})}),(0,a.jsx)(B.Z,{children:null!=e.expires?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.expires).toLocaleDateString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,a.jsx)(B.Z,{children:null!=e.max_budget?(0,a.jsx)(_.Z,{children:e.max_budget}):(0,a.jsx)(_.Z,{children:"Unlimited"})}),(0,a.jsx)(B.Z,{children:null!=e.budget_reset_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.budget_reset_at).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(a.Fragment,{children:n&&n.models&&n.models.length>0?n.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{size:"xs",onClick:()=>eb(e),children:"Edit Limits"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{onClick:()=>{ea(e),et(!0)},icon:O.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:es,onCancel:()=>{et(!1),ea(null)},footer:null,width:800,children:en&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-8",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(en.spend).toFixed(4)}catch(e){return en.spend}})()})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.max_budget?(0,a.jsxs)(a.Fragment,{children:[en.max_budget,en.budget_duration&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("br",{}),"Budget will be reset at ",en.budget_reset_at?new Date(en.budget_reset_at).toLocaleString():"Never"]})]}):(0,a.jsx)(a.Fragment,{children:"Unlimited"})})})]},e.name),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.expires?(0,a.jsx)(a.Fragment,{children:new Date(en.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,a.jsx)(a.Fragment,{children:"Never"})})})]},e.name)]}),(0,a.jsxs)(L.Z,{className:"my-4",children:[(0,a.jsx)(y.Z,{children:"Token Name"}),(0,a.jsx)(_.Z,{className:"my-1",children:en.key_alias?en.key_alias:en.key_name}),(0,a.jsx)(y.Z,{children:"Token ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.token}),(0,a.jsx)(y.Z,{children:"User ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.user_id}),(0,a.jsx)(y.Z,{children:"Metadata"}),(0,a.jsx)(_.Z,{className:"my-1",children:(0,a.jsxs)("pre",{children:[JSON.stringify(en.metadata)," "]})})]}),(0,a.jsx)(p.Z,{className:"mx-auto flex items-center",onClick:()=>{et(!1),ea(null)},children:"Close"})]})}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>ek(e)}),(0,a.jsx)(U.Z,{onClick:()=>eI(e),icon:F.Z,size:"sm"}),(0,a.jsx)(U.Z,{onClick:()=>ew(e),icon:M.Z,size:"sm"})]})]},e.token)})})]}),Z&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:eN,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{f(!1),I(null)},children:"Cancel"})]})]})]})})]}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:i}=e,[o]=k.Z.useForm(),[c,m]=(0,r.useState)(n),[u,h]=(0,r.useState)([]),[x,p]=(0,r.useState)(!1);return(0,a.jsx)(w.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{o.validateFields().then(e=>{o.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:o,onFinish:eS,initialValues:{...t,budget_duration:t.budget_duration},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let s=l.filter(e=>!c.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!c.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(s)),s.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(s,"Team models: ").concat(c.models)):Promise.resolve()}}],children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(J,{value:"all-team-models",children:"All Team Models"},"all-team-models"),c&&c.models?c.models.includes("all-proxy-models")?er.filter(e=>"all-proxy-models"!==e).map(e=>(0,a.jsx)(J,{value:e,children:e},e)):c.models.map(e=>(0,a.jsx)(J,{value:e,children:e},e)):er.map(e=>(0,a.jsx)(J,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==c?void 0:c.max_budget)!==null&&(null==c?void 0:c.max_budget)!==void 0?null==c?void 0:c.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.max_budget&&l>c.max_budget)throw console.log("keyTeam.max_budget: ".concat(c.max_budget)),Error("Budget cannot exceed team max budget: $".concat(c.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Current Reset Budget: ".concat(t.budget_duration,", budget will be reset: ").concat(t.budget_reset_at?new Date(t.budget_reset_at).toLocaleString():"Never"),children:(0,a.jsxs)(v.default,{placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"daily",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"weekly",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"monthly",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"token",name:"token",hidden:!0}),(0,a.jsx)(k.Z.Item,{label:"Team",name:"team_id",className:"mt-8",help:"the team this key belongs to",children:(0,a.jsx)(H.Z,{value:t.team_alias,children:null==d?void 0:d.map((e,l)=>(0,a.jsx)(G.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"TPM Limit (tokens per minute)",name:"tpm_limit",help:"tpm_limit cannot exceed team tpm_limit ".concat((null==c?void 0:c.tpm_limit)!==null&&(null==c?void 0:c.tpm_limit)!==void 0?null==c?void 0:c.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.tpm_limit&&l>c.tpm_limit)throw console.log("keyTeam.tpm_limit: ".concat(c.tpm_limit)),Error("tpm_limit cannot exceed team max tpm_limit: $".concat(c.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"RPM Limit (requests per minute)",name:"rpm_limit",help:"rpm_limit cannot exceed team max tpm_limit: ".concat((null==c?void 0:c.rpm_limit)!==null&&(null==c?void 0:c.rpm_limit)!==void 0?null==c?void 0:c.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.rpm_limit&&l>c.rpm_limit)throw console.log("keyTeam.rpm_limit: ".concat(c.rpm_limit)),Error("rpm_limit cannot exceed team max rpm_limit: $".concat(c.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:ee,onCancel:()=>{el(!1),ea(null)},token:en,onSubmit:eS}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:n,accessToken:i}=e,[o,d]=(0,r.useState)({}),[c,m]=(0,r.useState)([]),[h,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(t.metadata){let e=t.metadata.model_tpm_limit||{},l=t.metadata.model_rpm_limit||{},s={};Object.keys({...e,...l}).forEach(t=>{s[t]={tpm:e[t]||0,rpm:l[t]||0}}),d(s)}(async()=>{try{let e=await (0,u.AZ)(i,"",""),l=Array.from(new Set(e.data.map(e=>e.model_name)));m(l)}catch(e){console.error("Error fetching model data:",e),S.ZP.error("Failed to fetch available models")}})()},[t,i]);let j=(e,l,s)=>{d(t=>({...t,[e]:{...t[e],[l]:s||0}}))},g=e=>{d(l=>{let{[e]:s,...t}=l;return t})};return(0,a.jsxs)(w.Z,{title:"Edit Model-Specific Limits",visible:l,onCancel:s,footer:null,width:800,children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model"}),(0,a.jsx)(K.Z,{children:"TPM Limit"}),(0,a.jsx)(K.Z,{children:"RPM Limit"}),(0,a.jsx)(K.Z,{children:"Actions"})]})}),(0,a.jsxs)(z.Z,{children:[Object.entries(o).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:l}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.tpm,onChange:e=>j(l,"tpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.rpm,onChange:e=>j(l,"rpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>g(l),children:"Remove"})})]},l)}),null!==h&&(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(v.default,{style:{width:200},placeholder:"Select a model",onChange:e=>{o[e]||d(l=>({...l,[e]:{tpm:0,rpm:0}})),x(null)},value:h||void 0,children:c.filter(e=>!o.hasOwnProperty(e)).map(e=>(0,a.jsx)(J,{value:e,children:e},e))})}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>x(null),children:"Cancel"})})]})]})]}),(0,a.jsx)(p.Z,{onClick:()=>{x("")},disabled:null!==h,children:"Add Limit"})]}),(0,a.jsxs)("div",{className:"flex justify-end space-x-4 mt-6",children:[(0,a.jsx)(p.Z,{onClick:s,children:"Cancel"}),(0,a.jsx)(p.Z,{onClick:()=>{n({...t.metadata,model_tpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.tpm]})),model_rpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.rpm]}))})},children:"Save"})]})]})},{visible:ed,onCancel:()=>ec(!1),token:en,onSubmit:ev,accessToken:t}),(0,a.jsx)(w.Z,{title:"Regenerate API Key",visible:em,onCancel:()=>{eu(!1),eg.resetFields()},footer:[(0,a.jsx)(p.Z,{onClick:()=>{eu(!1),eg.resetFields()},className:"mr-2",children:"Cancel"},"cancel"),(0,a.jsx)(p.Z,{onClick:eC,disabled:!c,children:c?"Regenerate":"Upgrade to Regenerate"},"regenerate")],children:c?(0,a.jsxs)(k.Z,{form:eg,layout:"vertical",onValuesChange:(e,l)=>{"duration"in e&&eA("duration",e.duration)},children:[(0,a.jsx)(k.Z.Item,{name:"key_alias",label:"Key Alias",children:(0,a.jsx)(j.Z,{disabled:!0})}),(0,a.jsx)(k.Z.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,a.jsx)(A.Z,{step:.01,precision:2,style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"duration",label:"Expire Key (eg: 30s, 30h, 30d)",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsxs)("div",{className:"mt-2 text-sm text-gray-500",children:["Current expiry: ",(null==en?void 0:en.expires)!=null?new Date(en.expires).toLocaleString():"Never"]}),eZ&&(0,a.jsxs)("div",{className:"mt-2 text-sm text-green-600",children:["New expiry: ",eZ]})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",children:"Get Free Trial"})})]})}),eh&&(0,a.jsx)(w.Z,{visible:!!eh,onCancel:()=>ex(null),footer:[(0,a.jsx)(p.Z,{onClick:()=>ex(null),children:"Close"},"close")],children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Regenerated Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please replace your old key with the new key generated. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"Key Alias:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:(null==en?void 0:en.key_alias)||"No alias set"})}),(0,a.jsx)(_.Z,{className:"mt-3",children:"New API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:eh})}),(0,a.jsx)(b.CopyToClipboard,{text:eh,onCopy:()=>S.ZP.success("API Key copied to clipboard"),children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]})]})})]})};console.log=function(){};var X=e=>{let{userID:l,userRole:s,accessToken:t,userSpend:n,userMaxBudget:i,selectedTeam:o}=e;console.log("userSpend: ".concat(n));let[d,c]=(0,r.useState)(null!==n?n:0),[m,h]=(0,r.useState)(o?o.max_budget:null);(0,r.useEffect)(()=>{if(o){if("Default Team"===o.team_alias)h(i);else{let e=!1;if(o.team_memberships)for(let s of o.team_memberships)s.user_id===l&&"max_budget"in s.litellm_budget_table&&null!==s.litellm_budget_table.max_budget&&(h(s.litellm_budget_table.max_budget),e=!0);e||h(o.max_budget)}}},[o,i]);let[x,p]=(0,r.useState)([]);(0,r.useEffect)(()=>{let e=async()=>{if(!t||!l||!s)return};(async()=>{try{if(null===l||null===s)return;if(null!==t){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),p(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,t,l]),(0,r.useEffect)(()=>{null!==n&&c(n)},[n]);let j=[];o&&o.models&&(j=o.models),j&&j.includes("all-proxy-models")?(console.log("user models:",x),j=x):j&&j.includes("all-team-models")?j=o.models:j&&0===j.length&&(j=x);let g=void 0!==d?d.toFixed(4):null;return console.log("spend in view user spend: ".concat(d)),(0,a.jsx)("div",{className:"flex items-center",children:(0,a.jsxs)("div",{className:"flex justify-between gap-x-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Total Spend"}),(0,a.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",g]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Max Budget"}),(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null!==m?"$".concat(m," limit"):"No limit"})]})]})})};console.log=function(){};var $=e=>{let{userID:l,userRole:s,selectedTeam:t,accessToken:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==n){let e=(await (0,u.So)(n,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),o(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,s]);let d=[];return t&&t.models&&(d=t.models),d&&d.includes("all-proxy-models")&&(console.log("user models:",i),d=i),(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==t?void 0:t.team_alias}),(null==t?void 0:t.team_id)&&(0,a.jsxs)("p",{className:"text-xs text-gray-400 dark:text-gray-400 font-semibold",children:["Team ID: ",null==t?void 0:t.team_id]})]})})},Q=e=>{let l,{teams:s,setSelectedTeam:t,userRole:n,proxySettings:i,setProxySettings:o,userInfo:d,accessToken:c}=e;console.log("userInfo: ".concat(JSON.stringify(d)));let m={models:(null==d?void 0:d.models)||[],team_id:null,team_alias:"Default Team",max_budget:(null==d?void 0:d.max_budget)||null},h=async()=>{null===i&&c&&o(await (0,u.Dj)(c))};(0,r.useEffect)(()=>{h()},[i]);let[x,p]=(0,r.useState)(m);return console.log("userRole: ".concat(n)),console.log("proxySettings: ".concat(JSON.stringify(i))),l="App User"===n?s:i&&!0===i.DEFAULT_TEAM_DISABLED?s?[...s]:[m]:s?[...s,m]:[m],(0,a.jsxs)("div",{className:"mt-5 mb-5",children:[(0,a.jsx)(y.Z,{children:"Select Team"}),(0,a.jsx)(_.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,a.jsxs)(_.Z,{className:"mt-3 mb-3",children:[(0,a.jsx)("b",{children:"Default Team:"})," If no team_id is set for a key, it will be grouped under here."]}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>t(e),children:e.team_alias},l))}):(0,a.jsxs)(_.Z,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]})},ee=s(667),el=s(37963),es=s(97482);console.log=function(){},console.log("isLocal:",!1);var et=e=>{let{userID:l,userRole:s,teams:t,keys:n,setUserRole:o,userEmail:d,setUserEmail:c,setTeams:m,setKeys:p,premiumUser:j}=e,[g,Z]=(0,r.useState)(null),f=(0,i.useSearchParams)();f.get("viewSpend"),(0,i.useRouter)();let _=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),y=f.get("invitation_id"),[b,v]=(0,r.useState)(null),[k,S]=(0,r.useState)(null),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)(null),C={models:[],team_alias:"Default Team",team_id:null},[P,E]=(0,r.useState)(t?t[0]:C);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(_){let e=(0,el.o)(_);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),v(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"app_user":return"App User";case"internal_user":return"Internal User";case"internal_user_viewer":return"Internal Viewer";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),o(l)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&b&&s&&!n&&!g){let e=sessionStorage.getItem("userModels"+l);e?N(JSON.parse(e)):(async()=>{try{let e=await (0,u.Dj)(b);A(e);let t=await (0,u.Br)(b,l,s,!1,null,null);console.log("received teams in user dashboard: ".concat(Object.keys(t),"; team values: ").concat(Object.entries(t.teams))),Z(t.user_info),console.log("userSpendData: ".concat(JSON.stringify(g))),p(t.keys),m(t.teams);let n=[...t.teams];n.length>0?(console.log("response['teams']: ".concat(n)),E(n[0])):E(C),sessionStorage.setItem("userData"+l,JSON.stringify(t.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(t.user_info));let a=(await (0,u.So)(b,l,s)).data.map(e=>e.id);console.log("available_model_names:",a),N(a),console.log("userModels:",w),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,_,b,n,s]),(0,r.useEffect)(()=>{if(null!==n&&null!=P&&null!==P.team_id){let e=0;for(let l of n)P.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===P.team_id&&(e+=l.spend);S(e)}else if(null!==n){let e=0;for(let l of n)e+=l.spend;S(e)}},[P]),null!=y)return(0,a.jsx)(ee.default,{});if(null==l||null==_){let e="/sso/key/generate";return document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",console.log("Full URL:",e),window.location.href=e,null}if(null==b)return null;if(null==s&&o("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",P),(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)($,{userID:l,userRole:s,selectedTeam:P||null,accessToken:b}),(0,a.jsx)(X,{userID:l,userRole:s,userMaxBudget:(null==g?void 0:g.max_budget)||null,accessToken:b,userSpend:k,selectedTeam:P||null}),(0,a.jsx)(Y,{userID:l,userRole:s,accessToken:b,selectedTeam:P||null,data:n,setData:p,premiumUser:j,teams:t}),(0,a.jsx)(T,{userID:l,team:P||null,userRole:s,accessToken:b,data:n,setData:p},P?P.team_id:null),(0,a.jsx)(Q,{teams:t,setSelectedTeam:E,userRole:s,proxySettings:I,setProxySettings:A,userInfo:g,accessToken:b})]})})})},en=s(49167),ea=s(35087),er=s(92836),ei=s(26734),eo=s(41608),ed=s(32126),ec=s(23682),em=s(47047),eu=s(76628),eh=s(25707),ex=s(44041),ep=s(6180),ej=s(28683),eg=s(38302),eZ=s(66242),ef=s(78578),e_=s(34658),ey=e=>{let{modelID:l,accessToken:s}=e,[t,n]=(0,r.useState)(!1),i=async()=>{try{S.ZP.info("Making API Call"),n(!0);let e=await (0,u.Og)(s,l);console.log("model delete Response:",e),S.ZP.success("Model ".concat(l," deleted successfully")),n(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(U.Z,{onClick:()=>n(!0),icon:M.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:t,onOk:i,okType:"danger",onCancel:()=>n(!1),children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Delete Model"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Model ID: ",(0,a.jsx)("b",{children:l})]})})]})})]})},eb=s(97766),ev=s(46495),ek=s(18190),eS=s(91118),ew=e=>{let{modelMetrics:l,modelMetricsCategories:s,customTooltip:t,premiumUser:n}=e;return n?(0,a.jsx)(eS.Z,{title:"Time to First token (s)",className:"h-72",data:l,index:"date",showLegend:!1,categories:s,colors:["indigo","rose"],connectNulls:!0,customTooltip:t}):(0,a.jsxs)("div",{children:[(0,a.jsx)(ek.Z,{title:"✨ Enterprise Feature",color:"teal",className:"mt-2 mb-4",children:"Enterprise features are available for users with a specific license, please contact LiteLLM to unlock this limitation."}),(0,a.jsx)(p.Z,{variant:"primary",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get in touch"})})]})},eN=e=>{let{fields:l,selectedProvider:s}=e;return 0===l.length?null:(0,a.jsx)(a.Fragment,{children:l.map(e=>(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:e.field_name.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),name:e.field_name,tooltip:e.field_description,className:"mb-2",children:(0,a.jsx)(j.Z,{placeholder:e.field_value,type:"password"})},e.field_name))})},eI=s(67951);let{Title:eA,Link:eC}=es.default;(t=n||(n={})).OpenAI="OpenAI",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Studio",t.Anthropic="Anthropic",t.Google_AI_Studio="Google AI Studio",t.Bedrock="Amazon Bedrock",t.Groq="Groq",t.MistralAI="Mistral AI",t.Deepseek="Deepseek",t.OpenAI_Compatible="OpenAI-Compatible Endpoints (Together AI, etc.)",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.Cohere="Cohere",t.Databricks="Databricks",t.Ollama="Ollama";let eP={OpenAI:"openai",Azure:"azure",Azure_AI_Studio:"azure_ai",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",Groq:"groq",MistralAI:"mistral",Cohere:"cohere_chat",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai",Databricks:"databricks",Deepseek:"deepseek",Ollama:"ollama"},eT={"BadRequestError (400)":"BadRequestErrorRetries","AuthenticationError (401)":"AuthenticationErrorRetries","TimeoutError (408)":"TimeoutErrorRetries","RateLimitError (429)":"RateLimitErrorRetries","ContentPolicyViolationError (400)":"ContentPolicyViolationErrorRetries","InternalServerError (500)":"InternalServerErrorRetries"},eE=async(e,l,s)=>{try{let t=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(t)),console.log("received type of deployments: ".concat(typeof t)),t.forEach(async s=>{console.log("litellm_model: ".concat(s));let t={},n={};t.model=s;let a="";for(let[l,s]of(console.log("formValues add deployment:",e),Object.entries(e)))if(""!==s){if("model_name"==l)a+=s;else if("custom_llm_provider"==l){console.log("custom_llm_provider:",s);let e=eP[s];t.custom_llm_provider=e,console.log("custom_llm_provider mappingResult:",e)}else if("model"==l)continue;else if("base_model"===l)n[l]=s;else if("custom_model_name"===l)t.model=s;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",s);let e={};if(s&&void 0!=s){try{e=JSON.parse(s)}catch(e){throw S.ZP.error("Failed to parse LiteLLM Extra Params: "+e,10),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,s]of Object.entries(e))t[l]=s}}else t[l]=s}let r={model_name:a,litellm_params:t,model_info:n},i=await (0,u.kK)(l,r);console.log("response for model create call: ".concat(i.data))}),s.resetFields()}catch(e){S.ZP.error("Failed to create model: "+e,10)}};var eO=e=>{let l,{accessToken:s,token:t,userRole:i,userID:o,modelData:d={data:[]},keys:c,setModelData:m,premiumUser:h}=e,[g,Z]=(0,r.useState)([]),[f]=k.Z.useForm(),[b,v]=(0,r.useState)(null),[N,I]=(0,r.useState)(""),[P,T]=(0,r.useState)([]),E=Object.values(n).filter(e=>isNaN(Number(e))),[M,J]=(0,r.useState)([]),[Y,X]=(0,r.useState)("OpenAI"),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[et,ek]=(0,r.useState)(!1),[eS,eO]=(0,r.useState)(null),[eR,eF]=(0,r.useState)([]),[eM,eD]=(0,r.useState)([]),[eL,eU]=(0,r.useState)(null),[eV,ez]=(0,r.useState)([]),[eB,eq]=(0,r.useState)([]),[eK,eW]=(0,r.useState)([]),[eH,eG]=(0,r.useState)([]),[eJ,eY]=(0,r.useState)([]),[eX,e$]=(0,r.useState)([]),[eQ,e0]=(0,r.useState)([]),[e1,e2]=(0,r.useState)([]),[e4,e5]=(0,r.useState)([]),[e8,e3]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),[e6,e7]=(0,r.useState)(null),[e9,le]=(0,r.useState)(0),[ll,ls]=(0,r.useState)({}),[lt,ln]=(0,r.useState)([]),[la,lr]=(0,r.useState)(!1),[li,lo]=(0,r.useState)(null),[ld,lc]=(0,r.useState)(null),[lm,lu]=(0,r.useState)([]);(0,r.useEffect)(()=>{lS(eL,e8.from,e8.to)},[li,ld]);let lh=e=>{eO(e),el(!0)},lx=e=>{eO(e),ek(!0)},lp=async e=>{if(console.log("handleEditSubmit:",e),null==s)return;let l={},t=null;for(let[s,n]of(e.input_cost_per_million_tokens&&(e.input_cost_per_token=e.input_cost_per_million_tokens/1e6,delete e.input_cost_per_million_tokens),e.output_cost_per_million_tokens&&(e.output_cost_per_token=e.output_cost_per_million_tokens/1e6,delete e.output_cost_per_million_tokens),Object.entries(e)))"model_id"!==s?l[s]=n:t=n;let n={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",n);try{await (0,u.um)(s,n),S.ZP.success("Model updated successfully, restart server to see updates"),el(!1),eO(null)}catch(e){console.log("Error occurred")}},lj=()=>{I(new Date().toLocaleString())},lg=async()=>{if(!s){console.error("Access token is missing");return}console.log("new modelGroupRetryPolicy:",e6);try{await (0,u.K_)(s,{router_settings:{model_group_retry_policy:e6}}),S.ZP.success("Retry settings saved successfully")}catch(e){console.error("Failed to save retry settings:",e),S.ZP.error("Failed to save retry settings")}};if((0,r.useEffect)(()=>{if(!s||!t||!i||!o)return;let e=async()=>{try{var e,l,t,n,a,r,d,c,h,x,p,j;let g=await (0,u.hy)(s);J(g);let Z=await (0,u.AZ)(s,o,i);console.log("Model data response:",Z.data),m(Z);let f=new Set;for(let e=0;e0&&(y=_[_.length-1],console.log("_initial_model_group:",y)),console.log("selectedModelGroup:",eL);let b=await (0,u.o6)(s,o,i,y,null===(e=e8.from)||void 0===e?void 0:e.toISOString(),null===(l=e8.to)||void 0===l?void 0:l.toISOString(),null==li?void 0:li.token,ld);console.log("Model metrics response:",b),eq(b.data),eW(b.all_api_bases);let v=await (0,u.Rg)(s,y,null===(t=e8.from)||void 0===t?void 0:t.toISOString(),null===(n=e8.to)||void 0===n?void 0:n.toISOString());eG(v.data),eY(v.all_api_bases);let k=await (0,u.N8)(s,o,i,y,null===(a=e8.from)||void 0===a?void 0:a.toISOString(),null===(r=e8.to)||void 0===r?void 0:r.toISOString(),null==li?void 0:li.token,ld);console.log("Model exceptions response:",k),e$(k.data),e0(k.exception_types);let S=await (0,u.fP)(s,o,i,y,null===(d=e8.from)||void 0===d?void 0:d.toISOString(),null===(c=e8.to)||void 0===c?void 0:c.toISOString(),null==li?void 0:li.token,ld),w=await (0,u.n$)(s,null===(h=e8.from)||void 0===h?void 0:h.toISOString().split("T")[0],null===(x=e8.to)||void 0===x?void 0:x.toISOString().split("T")[0],y);ls(w);let N=await (0,u.v9)(s,null===(p=e8.from)||void 0===p?void 0:p.toISOString().split("T")[0],null===(j=e8.to)||void 0===j?void 0:j.toISOString().split("T")[0],y);ln(N),console.log("dailyExceptions:",w),console.log("dailyExceptionsPerDeplyment:",N),console.log("slowResponses:",S),e5(S);let I=await (0,u.j2)(s);lu(null==I?void 0:I.end_users);let A=(await (0,u.BL)(s,o,i)).router_settings;console.log("routerSettingsInfo:",A);let C=A.model_group_retry_policy,P=A.num_retries;console.log("model_group_retry_policy:",C),console.log("default_retries:",P),e7(C),le(P)}catch(e){console.error("There was an error fetching the model data",e)}};s&&t&&i&&o&&e();let l=async()=>{let e=await (0,u.qm)(s);console.log("received model cost map data: ".concat(Object.keys(e))),v(e)};null==b&&l(),lj()},[s,t,i,o,b,N]),!d||!s||!t||!i||!o)return(0,a.jsx)("div",{children:"Loading..."});let lZ=[],lf=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(b)),null!=b&&"object"==typeof b&&e in b)?b[e].litellm_provider:"openai";if(s){let e=s.split("/"),l=e[0];(a=t)||(a=1===e.length?u(s):l)}else a="-";n&&(r=null==n?void 0:n.input_cost_per_token,i=null==n?void 0:n.output_cost_per_token,o=null==n?void 0:n.max_tokens,c=null==n?void 0:n.max_input_tokens),(null==l?void 0:l.litellm_params)&&(m=Object.fromEntries(Object.entries(null==l?void 0:l.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),d.data[e].provider=a,d.data[e].input_cost=r,d.data[e].output_cost=i,d.data[e].litellm_model_name=s,lf.push(a),d.data[e].input_cost&&(d.data[e].input_cost=(1e6*Number(d.data[e].input_cost)).toFixed(2)),d.data[e].output_cost&&(d.data[e].output_cost=(1e6*Number(d.data[e].output_cost)).toFixed(2)),d.data[e].max_tokens=o,d.data[e].max_input_tokens=c,d.data[e].api_base=null==l?void 0:null===(lb=l.litellm_params)||void 0===lb?void 0:lb.api_base,d.data[e].cleanedLitellmParams=m,lZ.push(l.model_name),console.log(d.data[e])}if(d.data&&d.data.length>0&&d.data.sort((e,l)=>e.provider&&l.provider?e.provider.localeCompare(l.provider):e.provider&&!l.provider?-1:!e.provider&&l.provider?1:0),i&&"Admin Viewer"==i){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let lv=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(n).find(l=>n[l]===e);if(l){let e=eP[l];console.log("mappingResult: ".concat(e));let s=[];"object"==typeof b&&(Object.entries(b).forEach(l=>{let[t,n]=l;null!==n&&"object"==typeof n&&"litellm_provider"in n&&(n.litellm_provider===e||n.litellm_provider.includes(e))&&s.push(t)}),"Cohere"==l&&(console.log("adding cohere chat model"),Object.entries(b).forEach(e=>{let[l,t]=e;null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere"===t.litellm_provider&&s.push(l)}))),T(s),console.log("providerModels: ".concat(P))}},lk=async()=>{try{S.ZP.info("Running health check..."),Q("");let e=await (0,u.EY)(s);Q(e)}catch(e){console.error("Error running health check:",e),Q("Error running health check")}},lS=async(e,l,t)=>{if(console.log("Updating model metrics for group:",e),!s||!o||!i||!l||!t)return;console.log("inside updateModelMetrics - startTime:",l,"endTime:",t),eU(e);let n=null==li?void 0:li.token;void 0===n&&(n=null);let a=ld;void 0===a&&(a=null),l.setHours(0),l.setMinutes(0),l.setSeconds(0),t.setHours(23),t.setMinutes(59),t.setSeconds(59);try{let r=await (0,u.o6)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model metrics response:",r),eq(r.data),eW(r.all_api_bases);let d=await (0,u.Rg)(s,e,l.toISOString(),t.toISOString());eG(d.data),eY(d.all_api_bases);let c=await (0,u.N8)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model exceptions response:",c),e$(c.data),e0(c.exception_types);let m=await (0,u.fP)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);if(console.log("slowResponses:",m),e5(m),e){let n=await (0,u.n$)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ls(n);let a=await (0,u.v9)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ln(a)}}catch(e){console.error("Failed to fetch model metrics",e)}},lw=(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Select API Key Name"}),h?(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{lo(e)},children:e.key_alias},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lc(e)},children:e},l))]})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsxs)(G.Z,{value:String(l),disabled:!0,onClick:()=>{lo(e)},children:["✨ ",e.key_alias," (Enterprise only Feature)"]},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsxs)(G.Z,{value:e,disabled:!0,onClick:()=>{lc(e)},children:["✨ ",e," (Enterprise only Feature)"]},l))]})]})]}),lN=e=>{var l,s;let{payload:t,active:n}=e;if(!n||!t)return null;let r=null===(s=t[0])||void 0===s?void 0:null===(l=s.payload)||void 0===l?void 0:l.date,i=t.sort((e,l)=>l.value-e.value);if(i.length>5){let e=i.length-5;(i=i.slice(0,5)).push({dataKey:"".concat(e," other deployments"),value:t.slice(5).reduce((e,l)=>e+l.value,0),color:"gray"})}return(0,a.jsxs)("div",{className:"w-150 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r&&(0,a.jsxs)("p",{className:"text-tremor-content-emphasis mb-2",children:["Date: ",r]}),i.map((e,l)=>{let s=parseFloat(e.value.toFixed(5)),t=0===s&&e.value>0?"<0.00001":s.toFixed(5);return(0,a.jsxs)("div",{className:"flex justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("div",{className:"w-2 h-2 mt-1 rounded-full bg-".concat(e.color,"-500")}),(0,a.jsx)("p",{className:"text-tremor-content",children:e.dataKey})]}),(0,a.jsx)("p",{className:"font-medium text-tremor-content-emphasis text-righ ml-2",children:t})]},l)})]})},lI=e=>"Vertex AI (Anthropic, Gemini, etc.)"===e?"gemini-pro":"Anthropic"==e||"Amazon Bedrock"==e?"claude-3-opus":"Google AI Studio"==e?"gemini-pro":"Azure AI Studio"==e?"azure_ai/command-r-plus":"Azure"==e?"azure/my-deployment":"gpt-3.5-turbo";console.log("selectedProvider: ".concat(Y)),console.log("providerModels.length: ".concat(P.length));let lA=Object.keys(n).find(e=>n[e]===Y);return lA&&(l=M.find(e=>e.name===eP[lA])),(0,a.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(er.Z,{children:"All Models"}),(0,a.jsx)(er.Z,{children:"Add Model"}),(0,a.jsx)(er.Z,{children:(0,a.jsx)("pre",{children:"/health Models"})}),(0,a.jsx)(er.Z,{children:"Model Analytics"}),(0,a.jsx)(er.Z,{children:"Model Retry Settings"})]}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[N&&(0,a.jsxs)(_.Z,{children:["Last Refreshed: ",N]}),(0,a.jsx)(U.Z,{icon:F.Z,variant:"shadow",size:"xs",className:"self-center",onClick:lj})]})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsxs)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||void 0,onValueChange:e=>eU("all"===e?"all":e),value:eL||void 0,children:[(0,a.jsx)(G.Z,{value:"all",children:"All Models"}),eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))]})]}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{style:{maxWidth:"1500px",width:"100%"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Public Model Name"}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Provider"}),(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"LiteLLM Model"}),"Admin"===i&&(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"API Base"}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Input Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Output Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created At":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created At"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created By":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created By"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"50px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Status"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:d.data.filter(e=>"all"===eL||e.model_name===eL||null==eL||""===eL).map((e,l)=>{var t;return(0,a.jsxs)(W.Z,{style:{maxHeight:"1px",minHeight:"1px"},children:[(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.model_name||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.provider||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.litellm_model_name,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.litellm_model_name?e.litellm_model_name:"",children:e&&e.litellm_model_name?e.litellm_model_name.slice(0,20)+(e.litellm_model_name.length>20?"...":""):"-"})})}),"Admin"===i&&(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.api_base,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.api_base?e.api_base:"",children:e&&e.api_base?e.api_base.slice(0,20):"-"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.input_cost?e.input_cost:null!=e.litellm_params.input_cost_per_token&&void 0!=e.litellm_params.input_cost_per_token?(1e6*Number(e.litellm_params.input_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.output_cost?e.output_cost:e.litellm_params.output_cost_per_token?(1e6*Number(e.litellm_params.output_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&((t=e.model_info.created_at)?new Date(t).toLocaleDateString("en-US"):null)||"-"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&e.model_info.created_by||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:e.model_info.db_model?(0,a.jsx)(D.Z,{size:"xs",className:"text-white",children:(0,a.jsx)("p",{className:"text-xs",children:"DB Model"})}):(0,a.jsx)(D.Z,{size:"xs",className:"text-black",children:(0,a.jsx)("p",{className:"text-xs",children:"Config Model"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsxs)(x.Z,{numItems:3,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:O.Z,size:"sm",onClick:()=>lx(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>lh(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(ey,{modelID:e.model_info.id,accessToken:s})})]})})]},l)})})]})})]}),(0,a.jsx)(e=>{let{visible:l,onCancel:s,model:t,onSubmit:n}=e,[r]=k.Z.useForm(),i={},o="",d="";if(t){i=t.litellm_params,o=t.model_name;let e=t.model_info;e&&(d=e.id,console.log("model_id: ".concat(d)),i.model_id=d)}return(0,a.jsx)(w.Z,{title:"Edit Model "+o,visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n(e),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:lp,initialValues:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"organization",name:"organization",tooltip:"OpenAI Organization ID",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"tpm",name:"tpm",tooltip:"int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"rpm",name:"rpm",tooltip:"int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"max_retries",name:"max_retries",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"Input Cost per 1M Tokens",name:"input_cost_per_million_tokens",tooltip:"float (optional) - Input cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"Output Cost per 1M Tokens",name:"output_cost_per_million_tokens",tooltip:"float (optional) - Output cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:ee,onCancel:()=>{el(!1),eO(null)},model:eS,onSubmit:lp}),(0,a.jsxs)(w.Z,{title:eS&&eS.model_name,visible:et,width:800,footer:null,onCancel:()=>{ek(!1),eO(null)},children:[(0,a.jsx)(y.Z,{children:"Model Info"}),(0,a.jsx)(eI.Z,{language:"json",children:eS&&JSON.stringify(eS,null,2)})]})]}),(0,a.jsxs)(ed.Z,{className:"h-full",children:[(0,a.jsx)(eA,{level:2,children:"Add new model"}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(k.Z,{form:f,onFinish:()=>{f.validateFields().then(e=>{eE(e,s,f)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc.",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:Y.toString(),children:E.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lv(e),X(e)},children:e},l))})}),(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Public Model Name",name:"model_name",tooltip:"Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name.",className:"mb-0",children:(0,a.jsx)(j.Z,{})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,a.jsxs)(k.Z.Item,{label:"LiteLLM Model Name(s)",tooltip:"Actual model name used for making litellm.completion() / litellm.embedding() call.",className:"mb-0",children:[(0,a.jsx)(k.Z.Item,{name:"model",rules:[{required:!0,message:"Required"}],noStyle:!0,children:"Azure"===Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"===Y||"Ollama"===Y?(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())}):P.length>0?(0,a.jsxs)(em.Z,{children:[(0,a.jsx)(eu.Z,{value:"custom",children:"Custom Model Name (Enter below)"}),P.map((e,l)=>(0,a.jsx)(eu.Z,{value:e,children:e},l))]}):(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())})}),(0,a.jsx)(k.Z.Item,{noStyle:!0,shouldUpdate:(e,l)=>e.model!==l.model,children:e=>{let{getFieldValue:l}=e;return(l("model")||[]).includes("custom")&&(0,a.jsx)(k.Z.Item,{name:"custom_model_name",rules:[{required:!0,message:"Please enter a custom model name"}],className:"mt-2",children:(0,a.jsx)(j.Z,{placeholder:"Enter custom model name"})})}})]}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Actual model name used for making"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," ","models with the same 'public name'"]})})]}),void 0!==l&&l.fields.length>0&&(0,a.jsx)(eN,{fields:l.fields,selectedProvider:l.name}),"Amazon Bedrock"!=Y&&"Vertex AI (Anthropic, Gemini, etc.)"!=Y&&"Ollama"!=Y&&(void 0===l||0==l.fields.length)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,a.jsx)(j.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==Y&&(0,a.jsx)(k.Z.Item,{label:"Organization ID",name:"organization",children:(0,a.jsx)(j.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,a.jsx)(j.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,a.jsx)(ev.Z,{name:"file",accept:".json",beforeUpload:e=>{if("application/json"===e.type){let l=new FileReader;l.onload=e=>{if(e.target){let l=e.target.result;f.setFieldsValue({vertex_credentials:l})}},l.readAsText(e)}return!1},onChange(e){"uploading"!==e.file.status&&console.log(e.file,e.fileList),"done"===e.file.status?S.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&S.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,a.jsx)(C.ZP,{icon:(0,a.jsx)(eb.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"==Y)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,a.jsx)(j.Z,{placeholder:"https://..."})}),"Azure"==Y&&(0,a.jsx)(k.Z.Item,{label:"API Version",name:"api_version",tooltip:"By default litellm will use the latest version. If you want to use a different version, you can specify it here",children:(0,a.jsx)(j.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==Y&&(0,a.jsxs)("div",{children:[(0,a.jsx)(k.Z.Item,{label:"Base Model",name:"base_model",className:"mb-0",children:(0,a.jsx)(j.Z,{placeholder:"azure/gpt-3.5-turbo"})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-2",children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from"," ",(0,a.jsx)(eC,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})})]})]}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Access Key ID",name:"aws_access_key_id",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Secret Access Key",name:"aws_secret_access_key",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Region Name",name:"aws_region_name",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),(0,a.jsx)(k.Z.Item,{label:"LiteLLM Params",name:"litellm_extra_params",tooltip:"Optional litellm params used for making a litellm.completion() call.",className:"mb-0",children:(0,a.jsx)(ef.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,a.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Model"})}),(0,a.jsx)(ep.Z,{title:"Get help on our github",children:(0,a.jsx)(es.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,a.jsx)(p.Z,{onClick:lk,children:"Run `/health`"}),$&&(0,a.jsx)("pre",{children:JSON.stringify($,null,2)})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:4,className:"mt-2 mb-2",children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:e8,className:"mr-2",onValueChange:e=>{e3(e),lS(eL,e.from,e.to)}})]}),(0,a.jsxs)(ej.Z,{className:"ml-2",children:[(0,a.jsx)(_.Z,{children:"Select Model Group"}),(0,a.jsx)(H.Z,{defaultValue:eL||eR[0],value:eL||eR[0],children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>lS(e,e8.from,e8.to),children:e},l))})]}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(eZ.Z,{trigger:"click",content:lw,overlayStyle:{width:"20vw"},children:(0,a.jsx)(p.Z,{icon:e_.Z,size:"md",variant:"secondary",className:"mt-4 ml-2",style:{border:"none"},onClick:()=>lr(!0)})})})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"mr-2 max-h-[400px] min-h-[400px]",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Avg. Latency per Token"}),(0,a.jsx)(er.Z,{value:"2",children:"✨ Time to first token"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("p",{className:"text-gray-500 italic",children:" (seconds/token)"}),(0,a.jsx)(_.Z,{className:"text-gray-500 italic mt-1 mb-1",children:"average Latency for successfull requests divided by the total tokens"}),eB&&eK&&(0,a.jsx)(eh.Z,{title:"Model Latency",className:"h-72",data:eB,showLegend:!1,index:"date",categories:eK,connectNulls:!0,customTooltip:lN})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(ew,{modelMetrics:eH,modelMetricsCategories:eJ,customTooltip:lN,premiumUser:h})})]})]})})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"ml-2 max-h-[400px] min-h-[400px] overflow-y-auto",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Deployment"}),(0,a.jsx)(K.Z,{children:"Success Responses"}),(0,a.jsxs)(K.Z,{children:["Slow Responses ",(0,a.jsx)("p",{children:"Success Responses taking 600+s"})]})]})}),(0,a.jsx)(z.Z,{children:e4.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.api_base}),(0,a.jsx)(B.Z,{children:e.total_count}),(0,a.jsx)(B.Z,{children:e.slow_count})]},l))})]})})})]}),(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Exceptions for ",eL]}),(0,a.jsx)(ex.Z,{className:"h-60",data:eX,index:"model",categories:eQ,stack:!0,yAxisWidth:30})]})}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Up Rate Limit Errors (429) for ",eL]}),(0,a.jsxs)(x.Z,{numItems:1,children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",ll.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:ll.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]}),(0,a.jsx)(ej.Z,{})]})]}),h?(0,a.jsx)(a.Fragment,{children:lt.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base?e.api_base:"Unknown API Base"}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors (429) ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]},l))}):(0,a.jsx)(a.Fragment,{children:lt&<.length>0&<.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Rate Limit Errors by Deployment"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see exceptions for all deployments"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]})]},l))})]})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsx)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||eR[0],value:eL||eR[0],onValueChange:e=>eU(e),children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))})]}),(0,a.jsxs)(y.Z,{children:["Retry Policy for ",eL]}),(0,a.jsx)(_.Z,{className:"mb-6",children:"How many retries should be attempted based on the Exception"}),eT&&(0,a.jsx)("table",{children:(0,a.jsx)("tbody",{children:Object.entries(eT).map((e,l)=>{var s;let[t,n]=e,r=null==e6?void 0:null===(s=e6[eL])||void 0===s?void 0:s[n];return null==r&&(r=e9),(0,a.jsxs)("tr",{className:"flex justify-between items-center mt-2",children:[(0,a.jsx)("td",{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)("td",{children:(0,a.jsx)(A.Z,{className:"ml-5",value:r,min:0,step:1,onChange:e=>{e7(l=>{var s;let t=null!==(s=null==l?void 0:l[eL])&&void 0!==s?s:{};return{...null!=l?l:{},[eL]:{...t,[n]:e}}})}})})]},l)})})}),(0,a.jsx)(p.Z,{className:"mt-6 mr-8",onClick:lg,children:"Save"})]})]})]})})},eR=e=>{let{isInvitationLinkModalVisible:l,setIsInvitationLinkModalVisible:s,baseUrl:t,invitationLinkData:n}=e,{Title:r,Paragraph:i}=es.default,o=()=>(null==n?void 0:n.has_user_setup_sso)?"".concat(t,"/ui"):"".concat(t,"/ui?invitation_id=").concat(null==n?void 0:n.id);return(0,a.jsxs)(w.Z,{title:"Invitation Link",visible:l,width:800,footer:null,onOk:()=>{s(!1)},onCancel:()=>{s(!1)},children:[(0,a.jsx)(i,{children:"Copy and send the generated link to onboard this user to the proxy."}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"User ID"}),(0,a.jsx)(_.Z,{children:null==n?void 0:n.user_id})]}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{children:"Invitation Link"}),(0,a.jsx)(_.Z,{children:(0,a.jsx)(_.Z,{children:o()})})]}),(0,a.jsxs)("div",{className:"flex justify-end mt-5",children:[(0,a.jsx)("div",{}),(0,a.jsx)(b.CopyToClipboard,{text:o(),onCopy:()=>S.ZP.success("Copied!"),children:(0,a.jsx)(p.Z,{variant:"primary",children:"Copy invitation link"})})]})]})};let{Option:eF}=v.default;var eM=e=>{let{userID:l,accessToken:s,teams:t,possibleUIRoles:n}=e,[o,d]=(0,r.useState)(null),[c]=k.Z.useForm(),[m,h]=(0,r.useState)(!1),[x,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),[y,b]=(0,r.useState)(!1),[N,A]=(0,r.useState)(null),P=(0,i.useRouter)();console.log=function(){};let[T,E]=(0,r.useState)("");(0,r.useEffect)(()=>{(async()=>{try{let e=await (0,u.So)(s,l,"any"),t=[];for(let l=0;l{if(P){let{protocol:e,host:l}=window.location;E("".concat(e,"/").concat(l))}},[P]);let O=async e=>{try{var t;S.ZP.info("Making API Call"),h(!0),console.log("formValues in create user:",e);let n=await (0,u.Ov)(s,null,e);console.log("user create Response:",n),g(n.key);let a=(null===(t=n.data)||void 0===t?void 0:t.user_id)||n.user_id;if(null==o?void 0:o.SSO_ENABLED){let e={id:crypto.randomUUID(),user_id:a,is_accepted:!1,accepted_at:null,expires_at:new Date(Date.now()+6048e5),created_at:new Date,created_by:l,updated_at:new Date,updated_by:l,has_user_setup_sso:!0};A(e),b(!0)}else(0,u.XO)(s,a).then(e=>{e.has_user_setup_sso=!1,A(e),b(!0)});S.ZP.success("API user Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-0",onClick:()=>h(!0),children:"+ Invite User"}),(0,a.jsxs)(w.Z,{title:"Invite User",visible:m,width:800,footer:null,onOk:()=>{h(!1),c.resetFields()},onCancel:()=>{h(!1),g(null),c.resetFields()},children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Create a User who can own keys"}),(0,a.jsxs)(k.Z,{form:c,onFinish:O,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(k.Z.Item,{label:"User Email",name:"user_email",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:n&&Object.entries(n).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,a.jsx)(v.default,{placeholder:"Select Team ID",style:{width:"100%"},children:t?t.map(e=>(0,a.jsx)(eF,{value:e.team_id,children:e.team_alias},e.team_id)):(0,a.jsx)(eF,{value:null,children:"Default Team"},"default")})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create User"})})]})]}),x&&(0,a.jsx)(eR,{isInvitationLinkModalVisible:y,setIsInvitationLinkModalVisible:b,baseUrl:T,invitationLinkData:N})]})},eD=e=>{let{visible:l,possibleUIRoles:s,onCancel:t,user:n,onSubmit:i}=e,[o,d]=(0,r.useState)(n),[c]=k.Z.useForm();(0,r.useEffect)(()=>{c.resetFields()},[n]);let m=async()=>{c.resetFields(),t()},u=async e=>{i(e),c.resetFields(),t()};return n?(0,a.jsx)(w.Z,{visible:l,onCancel:m,footer:null,title:"Edit User "+n.user_id,width:1e3,children:(0,a.jsx)(k.Z,{form:c,onFinish:u,initialValues:n,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"User Email",tooltip:"Email of the User",name:"user_email",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"user_id",name:"user_id",hidden:!0,children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:s&&Object.entries(s).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Spend (USD)",name:"spend",tooltip:"(float) - Spend of all LLM calls completed by this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"User Budget (USD)",name:"max_budget",tooltip:"(float) - Maximum budget of this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})}):null};console.log=function(){};var eL=e=>{let{accessToken:l,token:s,keys:t,userRole:n,userID:i,teams:o,setKeys:d}=e,[c,m]=(0,r.useState)(null),[h,p]=(0,r.useState)(null),[j,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)(1),[_,y]=r.useState(null),[b,v]=(0,r.useState)(null),[k,w]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[A,C]=(0,r.useState)({}),P=async()=>{I(null),w(!1)},T=async e=>{if(console.log("inside handleEditSubmit:",e),l&&s&&n&&i){try{await (0,u.pf)(l,e,null),S.ZP.success("User ".concat(e.user_id," updated successfully"))}catch(e){console.error("There was an error updating the user",e)}h&&p(h.map(l=>l.user_id===e.user_id?e:l)),I(null),w(!1)}};return((0,r.useEffect)(()=>{if(!l||!s||!n||!i)return;let e=async()=>{try{let e=await (0,u.Br)(l,null,n,!0,Z,25);m(e),console.log("user data response:",e),p(e.users||[]);let s=await (0,u.lg)(l);C(s)}catch(e){console.error("There was an error fetching the model data",e)}};l&&s&&n&&i&&e()},[l,s,n,i,Z]),h&&l&&s&&n&&i)?(0,a.jsx)("div",{style:{width:"100%"},children:(0,a.jsxs)(x.Z,{className:"gap-2 p-2 h-[90vh] w-full mt-8",children:[(0,a.jsx)(eM,{userID:i,accessToken:l,teams:o,possibleUIRoles:A}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[90vh] mb-4",children:[(0,a.jsx)("div",{className:"mb-4 mt-1"}),(0,a.jsx)(ei.Z,{children:(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(V.Z,{className:"mt-5",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"User ID"}),(0,a.jsx)(K.Z,{children:"User Email"}),(0,a.jsx)(K.Z,{children:"Role"}),(0,a.jsx)(K.Z,{children:"User Spend ($ USD)"}),(0,a.jsx)(K.Z,{children:"User Max Budget ($ USD)"}),(0,a.jsx)(K.Z,{children:"API Keys"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:h.map(e=>{var l,s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_id||"-"}),(0,a.jsx)(B.Z,{children:e.user_email||"-"}),(0,a.jsx)(B.Z,{children:(null==A?void 0:null===(l=A[null==e?void 0:e.user_role])||void 0===l?void 0:l.ui_label)||"-"}),(0,a.jsx)(B.Z,{children:e.spend?null===(s=e.spend)||void 0===s?void 0:s.toFixed(2):"-"}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(x.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,a.jsxs)(D.Z,{size:"xs",color:"indigo",children:[e.key_aliases.filter(e=>null!==e).length,"\xa0Keys"]}):(0,a.jsx)(D.Z,{size:"xs",color:"gray",children:"No Keys"})})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,onClick:()=>{I(e),w(!0)},children:"View Keys"})})]},e.user_id)})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"flex-1"}),(0,a.jsx)("div",{className:"flex-1 flex justify-between items-center"})]})})]})}),(0,a.jsx)(eD,{visible:k,possibleUIRoles:A,onCancel:P,user:N,onSubmit:T})]}),function(){if(!h)return null;let e=(null==c?void 0:c.total_pages)||0,l=e=>{p([]),f(e)};return(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsxs)("div",{children:["Showing Page ",Z," of ",e]}),(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===Z,onClick:()=>l(Z-1),children:"← Prev"}),(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:Z===e,onClick:()=>l(Z+1),children:"Next →"})]})]})}()]})}):(0,a.jsx)("div",{children:"Loading..."})};console.log=function(){};var eU=e=>{let{teams:l,searchParams:s,accessToken:t,setTeams:n,userID:i,userRole:o}=e;(0,r.useEffect)(()=>{console.log("inside useeffect - ".concat(l)),null===l&&t&&(async()=>{let e=await (0,u.It)(t);console.log("givenTeams: ".concat(e)),n(e)})()},[l]);let[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:g}=es.default,[Z,f]=(0,r.useState)(""),[y,b]=(0,r.useState)(!1),[N,P]=(0,r.useState)(l?l[0]:null),[T,E]=(0,r.useState)(!1),[O,F]=(0,r.useState)(!1),[J,Y]=(0,r.useState)([]),[X,$]=(0,r.useState)(!1),[Q,ee]=(0,r.useState)(null),[el,et]=(0,r.useState)({}),en=e=>{P(e),b(!0)},ea=async e=>{let s=e.team_id;if(console.log("handleEditSubmit:",e),null==t)return;let a=await (0,u.Gh)(t,e);l&&n(l.map(e=>e.team_id===s?a.data:e)),S.ZP.success("Team updated successfully"),b(!1),P(null)},er=async e=>{ee(e),$(!0)},ei=async()=>{if(null!=Q&&null!=l&&null!=t){try{await (0,u.rs)(t,Q);let e=l.filter(e=>e.team_id!==Q);n(e)}catch(e){console.error("Error deleting the team:",e)}$(!1),ee(null)}};(0,r.useEffect)(()=>{let e=async()=>{try{if(null===i||null===o||null===t||null===l)return;let e={},s=await (0,u.It)(t);for(let l=0;l{try{if(null===i||null===o)return;if(null!==t){let e=(await (0,u.So)(t,i,o)).data.map(e=>e.id);console.log("available_model_names:",e),Y(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,i,o,l]);let eo=async e=>{try{if(null!=t){var s;let a=null==e?void 0:e.team_alias;if((null!==(s=null==l?void 0:l.map(e=>e.team_alias))&&void 0!==s?s:[]).includes(a))throw Error("Team alias ".concat(a," already exists, please pick another alias"));S.ZP.info("Creating Team");let r=await (0,u.hT)(t,e);null!==l?n([...l,r]):n([r]),console.log("response for team create call: ".concat(r)),S.ZP.success("Team created"),E(!1)}}catch(e){console.error("Error creating the team:",e),S.ZP.error("Error creating the team: "+e,20)}},ed=async e=>{try{if(null!=t&&null!=l){S.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},a=await (0,u.cu)(t,N.team_id,s);console.log("response for team create call: ".concat(a.data));let r=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(a.data.team_id)),e.team_id===a.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...l];e[r]=a.data,n(e),P(a.data)}F(!1)}}catch(e){console.error("Error creating the team:",e)}};return(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"All Teams"}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Team Name"}),(0,a.jsx)(K.Z,{children:"Team ID"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"TPM / RPM Limits"}),(0,a.jsx)(K.Z,{children:"Info"})]})}),(0,a.jsx)(z.Z,{children:l&&l.length>0?l.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",fontSize:"0.75em"},children:(0,a.jsx)(ep.Z,{title:e.team_id,children:e.team_id})}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!==e.max_budget&&void 0!==e.max_budget?e.max_budget:"No limit"}),(0,a.jsx)(B.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].keys&&el[e.team_id].keys.length," ","Keys"]}),(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].team_info&&el[e.team_id].team_info.members_with_roles&&el[e.team_id].team_info.members_with_roles.length," ","Members"]})]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>en(e)}),(0,a.jsx)(U.Z,{onClick:()=>er(e.team_id),icon:M.Z,size:"sm"})]})]},e.team_id)):null})]}),X&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:ei,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{$(!1),ee(null)},children:"Cancel"})]})]})]})})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>E(!0),children:"+ Create New Team"}),(0,a.jsx)(w.Z,{title:"Create Team",visible:T,width:800,footer:null,onOk:()=>{E(!1),d.resetFields()},onCancel:()=>{E(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:eo,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"Team Members"}),(0,a.jsx)(g,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{P(e)},children:e.team_alias},l))}):(0,a.jsxs)(g,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:N?N.members_with_roles.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsx)(B.Z,{children:e.role})]},l)):null})]})}),N&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,team:t,onSubmit:n}=e,[r]=k.Z.useForm();return(0,a.jsx)(w.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n({...e,team_id:t.team_id}),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:ea,initialValues:t,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J&&J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:y,onCancel:()=>{b(!1),P(null)},team:N,onSubmit:ea})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-5",onClick:()=>F(!0),children:"+ Add member"}),(0,a.jsx)(w.Z,{title:"Add member",visible:O,width:800,footer:null,onOk:()=>{F(!1),c.resetFields()},onCancel:()=>{F(!1),c.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:ed,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,a.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,a.jsx)(k.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eV=e=>{let l,{searchParams:s,accessToken:t,showSSOBanner:n,premiumUser:o}=e,[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:j}=es.default,[g,Z]=(0,r.useState)(""),[f,y]=(0,r.useState)(null),[b,v]=(0,r.useState)(null),[N,A]=(0,r.useState)(!1),[P,T]=(0,r.useState)(!1),[E,O]=(0,r.useState)(!1),[F,M]=(0,r.useState)(!1),[D,J]=(0,r.useState)(!1),[Y,X]=(0,r.useState)(!1),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(!1),[et,en]=(0,r.useState)(!1),[ea,er]=(0,r.useState)([]),[ei,eo]=(0,r.useState)(null),ed=(0,i.useRouter)(),[ec,em]=(0,r.useState)(null);console.log=function(){};let[eu,eh]=(0,r.useState)(""),ex="All IP Addresses Allowed";try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let ep=async()=>{try{if(!0!==o){S.ZP.error("This feature is only available for premium users. Please upgrade your account.");return}if(t){let e=await (0,u.PT)(t);er(e&&e.length>0?e:[ex])}else er([ex])}catch(e){console.error("Error fetching allowed IPs:",e),S.ZP.error("Failed to fetch allowed IPs ".concat(e)),er([ex])}finally{!0===o&&Q(!0)}},ej=async e=>{try{if(t){await (0,u.eH)(t,e.ip);let l=await (0,u.PT)(t);er(l),S.ZP.success("IP address added successfully")}}catch(e){console.error("Error adding IP:",e),S.ZP.error("Failed to add IP address ".concat(e))}finally{el(!1)}},eg=async e=>{eo(e),en(!0)},eZ=async()=>{if(ei&&t)try{await (0,u.$I)(t,ei);let e=await (0,u.PT)(t);er(e.length>0?e:[ex]),S.ZP.success("IP address deleted successfully")}catch(e){console.error("Error deleting IP:",e),S.ZP.error("Failed to delete IP address ".concat(e))}finally{en(!1),eo(null)}},ef=()=>{X(!1)},e_=["proxy_admin","proxy_admin_viewer"];(0,r.useEffect)(()=>{if(ed){let{protocol:e,host:l}=window.location;eh("".concat(e,"//").concat(l))}},[ed]),(0,r.useEffect)(()=>{(async()=>{if(null!=t){let e=[],l=await (0,u.Xd)(t,"proxy_admin_viewer");console.log("proxy admin viewer response: ",l);let s=l.users;console.log("proxy viewers response: ".concat(s)),s.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(s));let n=(await (0,u.Xd)(t,"proxy_admin")).users;n.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(n)),console.log("combinedList: ".concat(e)),y(e),em(await (0,u.lg)(t))}})()},[t]);let ey=()=>{M(!1),c.resetFields(),d.resetFields()},eb=()=>{M(!1),c.resetFields(),d.resetFields()},ev=e=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-8 mt-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},className:"mt-4",children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]}),eS=(e,l,s)=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:l,children:e_.map((e,l)=>(0,a.jsx)(G.Z,{value:e,children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:s,valuePropName:"user_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s,disabled:!0})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update role"})})]}),ew=async e=>{try{if(null!=t&&null!=f){S.ZP.info("Making API Call");let l=await (0,u.pf)(t,e,null);console.log("response for team create call: ".concat(l));let s=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(l.user_id)),e.user_id===l.user_id));console.log("foundIndex: ".concat(s)),-1==s&&(console.log("updates admin with new user"),f.push(l),y(f)),S.ZP.success("Refresh tab to see updated user role"),M(!1)}}catch(e){console.error("Error creating the key:",e)}},eN=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call");let s=await (0,u.pf)(t,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(s));let n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)});let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(s.user_id)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),T(!1)}}catch(e){console.error("Error creating the key:",e)}},eI=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call"),e.user_email,e.user_id;let s=await (0,u.pf)(t,e,"proxy_admin"),n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)}),console.log("response for team create call: ".concat(s));let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(n)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),O(!1)}}catch(e){console.error("Error creating the key:",e)}},eA=async e=>{if(null==t)return;let l={environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}};(0,u.K_)(t,l)};return console.log("admins: ".concat(null==f?void 0:f.length)),(0,a.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,a.jsx)(m,{level:4,children:"Admin Access "}),(0,a.jsxs)(j,{children:[n&&(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc."," ",(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:f?f.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsxs)(B.Z,{children:[" ",(null==ec?void 0:null===(s=ec[null==e?void 0:e.user_role])||void 0===s?void 0:s.ui_label)||"-"]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>M(!0)}),(0,a.jsx)(w.Z,{title:"Update role",visible:F,width:800,footer:null,onOk:ey,onCancel:eb,children:eS(ew,e.user_role,e.user_id)})]})]},l)}):null})]})})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("div",{className:"flex justify-start",children:[(0,a.jsx)(p.Z,{className:"mr-4 mb-5",onClick:()=>O(!0),children:"+ Add admin"}),(0,a.jsx)(w.Z,{title:"Add admin",visible:E,width:800,footer:null,onOk:()=>{O(!1),c.resetFields(),d.resetFields()},onCancel:()=>{O(!1),A(!1),c.resetFields(),d.resetFields()},children:ev(eI)}),(0,a.jsx)(eR,{isInvitationLinkModalVisible:N,setIsInvitationLinkModalVisible:A,baseUrl:eu,invitationLinkData:b}),(0,a.jsx)(p.Z,{className:"mb-5",onClick:()=>T(!0),children:"+ Add viewer"}),(0,a.jsx)(w.Z,{title:"Add viewer",visible:P,width:800,footer:null,onOk:()=>{T(!1),c.resetFields(),d.resetFields()},onCancel:()=>{T(!1),c.resetFields(),d.resetFields()},children:ev(eN)})]})})]}),(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(m,{level:4,children:" ✨ Security Settings"}),(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem",marginTop:"1rem"},children:[(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:()=>!0===o?J(!0):S.ZP.error("Only premium users can add SSO"),children:"Add SSO"})}),(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:ep,children:"Allowed IPs"})})]})]}),(0,a.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,a.jsx)(w.Z,{title:"Add SSO",visible:D,width:800,footer:null,onOk:()=>{J(!1),d.resetFields()},onCancel:()=>{J(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:e=>{eI(e),eA(e),J(!1),X(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,a.jsx)(I.Z.Password,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,a.jsx)(I.Z.Password,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,a.jsxs)(w.Z,{title:"SSO Setup Instructions",visible:Y,width:800,footer:null,onOk:ef,onCancel:()=>{X(!1)},children:[(0,a.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{onClick:ef,children:"Done"})})]}),(0,a.jsx)(w.Z,{title:"Manage Allowed IP Addresses",width:800,visible:$,onCancel:()=>Q(!1),footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>el(!0),children:"Add IP Address"},"add"),(0,a.jsx)(p.Z,{onClick:()=>Q(!1),children:"Close"},"close")],children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"IP Address"}),(0,a.jsx)(K.Z,{className:"text-right",children:"Action"})]})}),(0,a.jsx)(z.Z,{children:ea.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e}),(0,a.jsx)(B.Z,{className:"text-right",children:e!==ex&&(0,a.jsx)(p.Z,{onClick:()=>eg(e),color:"red",size:"xs",children:"Delete"})})]},l))})]})}),(0,a.jsx)(w.Z,{title:"Add Allowed IP Address",visible:ee,onCancel:()=>el(!1),footer:null,children:(0,a.jsxs)(k.Z,{onFinish:ej,children:[(0,a.jsx)(k.Z.Item,{name:"ip",rules:[{required:!0,message:"Please enter an IP address"}],children:(0,a.jsx)(I.Z,{placeholder:"Enter IP address"})}),(0,a.jsx)(k.Z.Item,{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add IP Address"})})]})}),(0,a.jsx)(w.Z,{title:"Confirm Delete",visible:et,onCancel:()=>en(!1),onOk:eZ,footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>eZ(),children:"Yes"},"delete"),(0,a.jsx)(p.Z,{onClick:()=>en(!1),children:"Close"},"close")],children:(0,a.jsxs)("p",{children:["Are you sure you want to delete the IP address: ",ei,"?"]})})]}),(0,a.jsxs)(ek.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access"," ",(0,a.jsxs)("a",{href:l,target:"_blank",children:[(0,a.jsx)("b",{children:l})," "]})]})]})]})},ez=s(42556),eB=s(90252),eq=e=>{let{alertingSettings:l,handleInputChange:s,handleResetField:t,handleSubmit:n,premiumUser:r}=e,[i]=k.Z.useForm();return(0,a.jsxs)(k.Z,{form:i,onFinish:()=>{console.log("INSIDE ONFINISH");let e=i.getFieldsValue(),l=Object.entries(e).every(e=>{let[l,s]=e;return"boolean"!=typeof s&&(""===s||null==s)});console.log("formData: ".concat(JSON.stringify(e),", isEmpty: ").concat(l)),l?console.log("Some form fields are empty."):n(e)},labelAlign:"left",children:[l.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{align:"center",children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?r?(0,a.jsx)(k.Z.Item,{name:e.field_name,children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l)}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>s(e.field_name,l)}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}):(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,a.jsx)(k.Z.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l),className:"p-0"}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>{s(e.field_name,l),i.setFieldsValue({[e.field_name]:l})}}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>t(e.field_name,l),children:"Reset"})})]},l)),(0,a.jsx)("div",{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update Settings"})})]})},eK=e=>{let{accessToken:l,premiumUser:s}=e,[t,n]=(0,r.useState)([]);return(0,r.useEffect)(()=>{l&&(0,u.RQ)(l).then(e=>{n(e)})},[l]),(0,a.jsx)(eq,{alertingSettings:t,handleInputChange:(e,l)=>{let s=t.map(s=>s.field_name===e?{...s,field_value:l}:s);console.log("updatedSettings: ".concat(JSON.stringify(s))),n(s)},handleResetField:(e,s)=>{if(l)try{let l=t.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:l.field_default_value}:l);n(l)}catch(e){console.log("ERROR OCCURRED!")}},handleSubmit:e=>{if(!l||(console.log("formValues: ".concat(e)),null==e||void 0==e))return;let s={};t.forEach(e=>{s[e.field_name]=e.field_value});let n={...e,...s};console.log("mergedFormValues: ".concat(JSON.stringify(n)));let{slack_alerting:a,...r}=n;console.log("slack_alerting: ".concat(a,", alertingArgs: ").concat(JSON.stringify(r)));try{(0,u.jA)(l,"alerting_args",r),"boolean"==typeof a&&(!0==a?(0,u.jA)(l,"alerting",["slack"]):(0,u.jA)(l,"alerting",[])),S.ZP.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:s})},eW=s(84406);let{Title:eH,Paragraph:eG}=es.default;console.log=function(){};var eJ=e=>{let{accessToken:l,userRole:s,userID:t,premiumUser:n}=e,[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)([]),[m,h]=(0,r.useState)(!1),[g]=k.Z.useForm(),[Z,f]=(0,r.useState)(null),[y,b]=(0,r.useState)([]),[N,I]=(0,r.useState)(""),[A,P]=(0,r.useState)({}),[T,E]=(0,r.useState)([]),[O,F]=(0,r.useState)(!1),[M,D]=(0,r.useState)([]),[H,J]=(0,r.useState)(null),[Y,X]=(0,r.useState)([]),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(null),es=e=>{T.includes(e)?E(T.filter(l=>l!==e)):E([...T,e])},et={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,r.useEffect)(()=>{l&&s&&t&&(0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e),o(e.callbacks),D(e.available_callbacks);let l=e.alerts;if(console.log("alerts_data",l),l&&l.length>0){let e=l[0];console.log("_alert_info",e);let s=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",s),E(e.active_alerts),I(s),P(e.alerts_to_webhook)}c(l)})},[l,s,t]);let en=e=>T&&T.includes(e),ea=()=>{if(!l)return;let e={};d.filter(e=>"email"===e.name).forEach(l=>{var s;Object.entries(null!==(s=l.variables)&&void 0!==s?s:{}).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));n&&n.value&&(e[s]=null==n?void 0:n.value)})}),console.log("updatedVariables",e);try{(0,u.K_)(l,{general_settings:{alerting:["email"]},environment_variables:e})}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Email settings updated successfully")},em=async e=>{if(!l)return;let s={};Object.entries(e).forEach(e=>{let[l,t]=e;"callback"!==l&&(s[l]=t)});try{await (0,u.K_)(l,{environment_variables:s}),S.ZP.success("Callback added successfully"),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eu=async e=>{if(!l)return;let s=null==e?void 0:e.callback,t={};Object.entries(e).forEach(e=>{let[l,s]=e;"callback"!==l&&(t[l]=s)});try{await (0,u.K_)(l,{environment_variables:t,litellm_settings:{success_callback:[s]}}),S.ZP.success("Callback ".concat(s," added successfully")),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eh=e=>{console.log("inside handleSelectedCallbackChange",e),f(e.litellm_callback_name),console.log("all callbacks",M),e&&e.litellm_callback_params?(X(e.litellm_callback_params),console.log("selectedCallbackParams",Y)):X([])};return l?(console.log("callbacks: ".concat(i)),(0,a.jsxs)("div",{className:"w-full mx-4",children:[(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Logging Callbacks"}),(0,a.jsx)(er.Z,{value:"2",children:"Alerting Types"}),(0,a.jsx)(er.Z,{value:"3",children:"Alerting Settings"}),(0,a.jsx)(er.Z,{value:"4",children:"Email Alerts"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Active Logging Callbacks"}),(0,a.jsx)(x.Z,{numItems:2,children:(0,a.jsx)(L.Z,{className:"max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(K.Z,{children:"Callback Name"})})}),(0,a.jsx)(z.Z,{children:i.map((e,s)=>(0,a.jsxs)(W.Z,{className:"flex justify-between",children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.name})}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"flex justify-between",children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>{el(e),Q(!0)}}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,e.name),className:"ml-2",variant:"secondary",children:"Test Callback"})]})})]},s))})]})})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>F(!0),children:"Add Callback"})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(_.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,a.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{children:"Slack Webhook URL"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(et).map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:"region_outage_alerts"==s?n?(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)}):(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:s,type:"password",defaultValue:A&&A[s]?A[s]:N})})]},l)})})]}),(0,a.jsx)(p.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(et).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));console.log("key",s),console.log("webhookInput",n);let a=(null==n?void 0:n.value)||"";console.log("newWebhookValue",a),e[s]=a}),console.log("updatedAlertToWebhooks",e);let s={general_settings:{alert_to_webhook_url:e,alert_types:T}};console.log("payload",s);try{(0,u.K_)(l,s)}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"slack"),className:"mx-2",children:"Test Alerts"})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eK,{accessToken:l,premiumUser:n})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Email Settings"}),(0,a.jsxs)(_.Z,{children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: email alerts"})," ",(0,a.jsx)("br",{})]}),(0,a.jsx)("div",{className:"flex w-full",children:d.filter(e=>"email"===e.name).map((e,l)=>{var s;return(0,a.jsx)(B.Z,{children:(0,a.jsx)("ul",{children:(0,a.jsx)(x.Z,{numItems:2,children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).map(e=>{let[l,s]=e;return(0,a.jsxs)("li",{className:"mx-2 my-2",children:[!0!=n&&("EMAIL_LOGO_URL"===l||"EMAIL_SUPPORT_CONTACT"===l)?(0,a.jsxs)("div",{children:[(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,a.jsxs)(_.Z,{className:"mt-2",children:[" ","✨ ",l]})}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-2",children:l}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",style:{width:"400px"}})]}),(0,a.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===l&&(0,a.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},l)})})})},l)})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>ea(),children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"email"),className:"mx-2",children:"Test Email Alerts"})]})})]})]})}),(0,a.jsxs)(w.Z,{title:"Add Logging Callback",visible:O,width:800,onCancel:()=>F(!1),footer:null,children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: Logging"}),(0,a.jsx)(k.Z,{form:g,onFinish:eu,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(eW.Z,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,a.jsx)(v.default,{onChange:e=>{let l=M[e];l&&(console.log(l.ui_callback_name),eh(l))},children:M&&Object.values(M).map(e=>(0,a.jsx)(G.Z,{value:e.litellm_callback_name,children:e.ui_callback_name},e.litellm_callback_name))})}),Y&&Y.map(e=>(0,a.jsx)(eW.Z,{label:e,name:e,rules:[{required:!0,message:"Please enter the value for "+e}],children:(0,a.jsx)(j.Z,{type:"password"})},e)),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]}),(0,a.jsx)(w.Z,{visible:$,width:800,title:"Edit ".concat(null==ee?void 0:ee.name," Settings"),onCancel:()=>Q(!1),footer:null,children:(0,a.jsxs)(k.Z,{form:g,onFinish:em,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:ee&&ee.variables&&Object.entries(ee.variables).map(e=>{let[l,s]=e;return(0,a.jsx)(eW.Z,{label:l,name:l,children:(0,a.jsx)(j.Z,{type:"password",defaultValue:s})},l)})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:eY}=v.default;var eX=e=>{let{models:l,accessToken:s,routerSettings:t,setRouterSettings:n}=e,[i]=k.Z.useForm(),[o,d]=(0,r.useState)(!1),[c,m]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>d(!0),children:"+ Add Fallbacks"}),(0,a.jsx)(w.Z,{title:"Add Fallbacks",visible:o,width:800,footer:null,onOk:()=>{d(!1),i.resetFields()},onCancel:()=>{d(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:e=>{console.log(e);let{model_name:l,models:a}=e,r=[...t.fallbacks||[],{[l]:a}],o={...t,fallbacks:r};console.log(o);try{(0,u.K_)(s,{router_settings:o}),n(o)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully"),d(!1),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,a.jsx)(H.Z,{defaultValue:c,children:l&&l.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsx)(em.Z,{value:l,children:l&&l.filter(e=>e!=c).map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},e$=s(12968);async function eQ(e,l){console.log=function(){},console.log("isLocal:",!1);let s=window.location.origin,t=new e$.ZP.OpenAI({apiKey:l,baseURL:s,dangerouslyAllowBrowser:!0});try{let l=await t.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.ZP.success((0,a.jsxs)("span",{children:["Test model=",(0,a.jsx)("strong",{children:e}),", received model=",(0,a.jsx)("strong",{children:l.model}),". See"," ",(0,a.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}let e0={ttl:3600,lowest_latency_buffer:0},e1=e=>{let{selectedStrategy:l,strategyArgs:s,paramExplanation:t}=e;return(0,a.jsxs)(g.Z,{children:[(0,a.jsx)(f.Z,{className:"text-sm font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong",children:"Routing Strategy Specific Args"}),(0,a.jsx)(Z.Z,{children:"latency-based-routing"==l?(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(s).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:t[l]})]}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]})}):(0,a.jsx)(_.Z,{children:"No specific settings"})})]})};var e2=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)({}),[d,c]=(0,r.useState)({}),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(!1),[b]=k.Z.useForm(),[v,w]=(0,r.useState)(null),[N,I]=(0,r.useState)(null),[C,P]=(0,r.useState)(null),T={routing_strategy_args:"(dict) Arguments to pass to the routing strategy",routing_strategy:"(string) Routing strategy to use",allowed_fails:"(int) Number of times a deployment can fail before being added to cooldown",cooldown_time:"(int) time in seconds to cooldown a deployment after failure",num_retries:"(int) Number of retries for failed requests. Defaults to 0.",timeout:"(float) Timeout for requests. Defaults to None.",retry_after:"(int) Minimum time to wait before retrying a failed request",ttl:"(int) Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"(float) Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};(0,r.useEffect)(()=>{l&&s&&t&&((0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e);let l=e.router_settings;"model_group_retry_policy"in l&&delete l.model_group_retry_policy,o(l)}),(0,u.YU)(l).then(e=>{g(e)}))},[l,s,t]);let E=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(i.fallbacks)),i.fallbacks.map(l=>(e in l&&delete l[e],l));try{await (0,u.K_)(l,{router_settings:i}),o({...i}),I(i.routing_strategy),S.ZP.success("Router settings updated successfully")}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}}},O=(e,l)=>{g(m.map(s=>s.field_name===e?{...s,field_value:l}:s))},R=(e,s)=>{if(!l)return;let t=m[s].field_value;if(null!=t&&void 0!=t)try{(0,u.jA)(l,e,t);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:!0}:l);g(s)}catch(e){}},F=(e,s)=>{if(l)try{(0,u.ao)(l,e);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:null}:l);g(s)}catch(e){}},J=e=>{if(!l)return;console.log("router_settings",e);let s=Object.fromEntries(Object.entries(e).map(e=>{let[l,s]=e;if("routing_strategy_args"!==l&&"routing_strategy"!==l){var t;return[l,(null===(t=document.querySelector('input[name="'.concat(l,'"]')))||void 0===t?void 0:t.value)||s]}if("routing_strategy"==l)return[l,N];if("routing_strategy_args"==l&&"latency-based-routing"==N){let e={},l=document.querySelector('input[name="lowest_latency_buffer"]'),s=document.querySelector('input[name="ttl"]');return(null==l?void 0:l.value)&&(e.lowest_latency_buffer=Number(l.value)),(null==s?void 0:s.value)&&(e.ttl=Number(s.value)),console.log("setRoutingStrategyArgs: ".concat(e)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));console.log("updatedVariables",s);try{(0,u.K_)(l,{router_settings:s})}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully")};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Loadbalancing"}),(0,a.jsx)(er.Z,{value:"2",children:"Fallbacks"}),(0,a.jsx)(er.Z,{value:"3",children:"General"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,a.jsx)(y.Z,{children:"Router Settings"}),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(i).filter(e=>{let[l,s]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l&&"routing_strategy_args"!=l}).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:T[l]})]}),(0,a.jsx)(B.Z,{children:"routing_strategy"==l?(0,a.jsxs)(H.Z,{defaultValue:s,className:"w-full max-w-md",onValueChange:I,children:[(0,a.jsx)(G.Z,{value:"usage-based-routing",children:"usage-based-routing"}),(0,a.jsx)(G.Z,{value:"latency-based-routing",children:"latency-based-routing"}),(0,a.jsx)(G.Z,{value:"simple-shuffle",children:"simple-shuffle"})]}):(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]}),(0,a.jsx)(e1,{selectedStrategy:N,strategyArgs:i&&i.routing_strategy_args&&Object.keys(i.routing_strategy_args).length>0?i.routing_strategy_args:e0,paramExplanation:T})]}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>J(i),children:"Save Changes"})})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model Name"}),(0,a.jsx)(K.Z,{children:"Fallbacks"})]})}),(0,a.jsx)(z.Z,{children:i.fallbacks&&i.fallbacks.map((e,s)=>Object.entries(e).map(e=>{let[t,n]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:t}),(0,a.jsx)(B.Z,{children:Array.isArray(n)?n.join(", "):n}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>eQ(t,l),children:"Test Fallback"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>E(t)})})]},s.toString()+t)}))})]}),(0,a.jsx)(eX,{models:(null==n?void 0:n.data)?n.data.map(e=>e.model_name):[],accessToken:l,routerSettings:i,setRouterSettings:o})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"}),(0,a.jsx)(K.Z,{children:"Status"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:m.filter(e=>"TypedDictionary"!==e.field_type).map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),(0,a.jsx)(B.Z,{children:"Integer"==e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>O(e.field_name,l)}):null}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(p.Z,{onClick:()=>R(e.field_name,l),children:"Update"}),(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>F(e.field_name,l),children:"Reset"})]})]},l))})]})})})]})]})}):null},e4=s(98786),e5=s(74325),e8=e=>{let{value:l={},onChange:s}=e,[t,n]=(0,r.useState)(Object.entries(l)),i=e=>{let l=t.filter((l,s)=>s!==e);n(l),null==s||s(Object.fromEntries(l))},o=(e,l,a)=>{let r=[...t];r[e]=[l,a],n(r),null==s||s(Object.fromEntries(r))};return(0,a.jsxs)("div",{children:[t.map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(c.Z,{style:{display:"flex",marginBottom:8},align:"start",children:[(0,a.jsx)(j.Z,{placeholder:"Header Name",value:s,onChange:e=>o(l,e.target.value,t)}),(0,a.jsx)(j.Z,{placeholder:"Header Value",value:t,onChange:e=>o(l,s,e.target.value)}),(0,a.jsx)(e4.Z,{onClick:()=>i(l)})]},l)}),(0,a.jsx)(C.ZP,{type:"dashed",onClick:()=>{n([...t,["",""]])},icon:(0,a.jsx)(e5.Z,{}),children:"Add Header"})]})};let{Option:e3}=v.default;var e6=e=>{let{accessToken:l,setPassThroughItems:s,passThroughItems:t}=e,[n]=k.Z.useForm(),[i,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>o(!0),children:"+ Add Pass-Through Endpoint"}),(0,a.jsx)(w.Z,{title:"Add Pass-Through Endpoint",visible:i,width:800,footer:null,onOk:()=>{o(!1),n.resetFields()},onCancel:()=>{o(!1),n.resetFields()},children:(0,a.jsxs)(k.Z,{form:n,onFinish:e=>{console.log(e);let a=[...t,{headers:e.headers,path:e.path,target:e.target}];try{(0,u.Vt)(l,e),s(a)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("Pass through endpoint successfully added"),o(!1),n.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Path",name:"path",rules:[{required:!0,message:"The route to be added to the LiteLLM Proxy Server."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Target",name:"target",rules:[{required:!0,message:"The URL to which requests for this path should be forwarded."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Headers",name:"headers",rules:[{required:!0,message:"Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint"}],help:"required",children:(0,a.jsx)(e8,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Pass-Through Endpoint"})})]})})]})},e7=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&(0,u.mp)(l).then(e=>{o(e.endpoints)})},[l,s,t]);let d=(e,s)=>{if(l)try{(0,u.EG)(l,e);let s=i.filter(l=>l.path!==e);o(s),S.ZP.success("Endpoint deleted successfully.")}catch(e){}};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Path"}),(0,a.jsx)(K.Z,{children:"Target"}),(0,a.jsx)(K.Z,{children:"Headers"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:i.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.path})}),(0,a.jsx)(B.Z,{children:e.target}),(0,a.jsx)(B.Z,{children:JSON.stringify(e.headers)}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>d(e.path,l),children:"Reset"})})]},l))})]}),(0,a.jsx)(e6,{accessToken:l,setPassThroughItems:o,passThroughItems:i})]})})}):null},e9=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n}=e,[r]=k.Z.useForm(),i=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),r.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Create Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),r.resetFields()},onCancel:()=>{t(!1),r.resetFields()},children:(0,a.jsxs)(k.Z,{form:r,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Budget"})})]})})},le=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n,existingBudget:r}=e,[i]=k.Z.useForm(),o=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),i.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Edit Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),i.resetFields()},onCancel:()=>{t(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:r,children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Budget"})})]})})},ll=e=>{let{accessToken:l}=e,[s,t]=(0,r.useState)(!1),[n,i]=(0,r.useState)(!1),[o,d]=(0,r.useState)(null),[c,m]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&(0,u.O3)(l).then(e=>{m(e)})},[l]);let h=async(e,s)=>{null!=l&&(d(c[s]),i(!0))},x=async(e,s)=>{if(null==l)return;S.ZP.info("Request made"),await (0,u.NV)(l,e);let t=[...c];t.splice(s,1),m(t),S.ZP.success("Budget Deleted.")};return(0,a.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,a.jsx)(p.Z,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>t(!0),children:"+ Create Budget"}),(0,a.jsx)(e9,{accessToken:l,isModalVisible:s,setIsModalVisible:t,setBudgetList:m}),o&&(0,a.jsx)(le,{accessToken:l,isModalVisible:n,setIsModalVisible:i,setBudgetList:m,existingBudget:o}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"Create a budget to assign to customers."}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Budget ID"}),(0,a.jsx)(K.Z,{children:"Max Budget"}),(0,a.jsx)(K.Z,{children:"TPM"}),(0,a.jsx)(K.Z,{children:"RPM"})]})}),(0,a.jsx)(z.Z,{children:c.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.budget_id}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"n/a"}),(0,a.jsx)(B.Z,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,a.jsx)(B.Z,{children:e.rpm_limit?e.rpm_limit:"n/a"}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>h(e.budget_id,l)}),(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>x(e.budget_id,l)})]},l))})]})]}),(0,a.jsxs)("div",{className:"mt-5",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"How to use budget id"}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"Assign Budget to Customer"}),(0,a.jsx)(er.Z,{children:"Test it (Curl)"}),(0,a.jsx)(er.Z,{children:"Test it (OpenAI SDK)"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:"\ncurl -X POST --location '/end_user/new' \n-H 'Authorization: Bearer ' \n-H 'Content-Type: application/json' \n-d '{\"user_id\": \"my-customer-id', \"budget_id\": \"\"}' # \uD83D\uDC48 KEY CHANGE\n\n "})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:'\ncurl -X POST --location \'/chat/completions\' \n-H \'Authorization: Bearer \' \n-H \'Content-Type: application/json\' \n-d \'{\n "model": "gpt-3.5-turbo\', \n "messages":[{"role": "user", "content": "Hey, how\'s it going?"}],\n "user": "my-customer-id"\n}\' # \uD83D\uDC48 KEY CHANGE\n\n '})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'from openai import OpenAI\nclient = OpenAI(\n base_url="",\n api_key=""\n)\n\ncompletion = client.chat.completions.create(\n model="gpt-3.5-turbo",\n messages=[\n {"role": "system", "content": "You are a helpful assistant."},\n {"role": "user", "content": "Hello!"}\n ],\n user="my-customer-id"\n)\n\nprint(completion.choices[0].message)'})})]})]})]})]})},ls=s(41134),lt=e=>{let{proxySettings:l}=e,s="";return l&&l.PROXY_BASE_URL&&void 0!==l.PROXY_BASE_URL&&(s=l.PROXY_BASE_URL),(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,a.jsx)(_.Z,{className:"mt-2 mb-2",children:"LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below "}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"OpenAI Python SDK"}),(0,a.jsx)(er.Z,{children:"LlamaIndex"}),(0,a.jsx)(er.Z,{children:"Langchain Py"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="'.concat(s,'" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="'.concat(s,'", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="').concat(s,'",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="'.concat(s,'",\n model = "gpt-3.5-turbo",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})})};async function ln(e,l,s,t){console.log=function(){},console.log("isLocal:",!1);let n=window.location.origin,a=new e$.ZP.OpenAI({apiKey:t,baseURL:n,dangerouslyAllowBrowser:!0});try{for await(let t of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(t),t.choices[0].delta.content&&l(t.choices[0].delta.content)}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var la=e=>{let{accessToken:l,token:s,userRole:t,userID:n}=e,[i,o]=(0,r.useState)(""),[d,c]=(0,r.useState)(""),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(void 0),[y,b]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&n&&(async()=>{try{let e=await (0,u.So)(l,n,t);if(console.log("model_info:",e),(null==e?void 0:e.data.length)>0){let l=e.data.map(e=>({value:e.id,label:e.id}));if(console.log(l),l.length>0){let e=Array.from(new Set(l));console.log("Unique models:",e),e.sort((e,l)=>e.label.localeCompare(l.label)),console.log("Model info:",y),b(e)}f(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,n,t]);let k=(e,l)=>{g(s=>{let t=s[s.length-1];return t&&t.role===e?[...s.slice(0,s.length-1),{role:e,content:t.content+l}]:[...s,{role:e,content:l}]})},S=async()=>{if(""!==d.trim()&&i&&s&&t&&n){g(e=>[...e,{role:"user",content:d}]);try{Z&&await ln(d,e=>k("assistant",e),Z,i)}catch(e){console.error("Error fetching model response",e),k("assistant","Error fetching model response")}c("")}};if(t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,a.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsx)(eo.Z,{children:(0,a.jsx)(er.Z,{children:"Chat"})}),(0,a.jsx)(ec.Z,{children:(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("div",{className:"sm:max-w-2xl",children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"API Key"}),(0,a.jsx)(j.Z,{placeholder:"Type API Key here",type:"password",onValueChange:o,value:i})]}),(0,a.jsxs)(h.Z,{className:"mx-2",children:[(0,a.jsx)(_.Z,{children:"Select Model:"}),(0,a.jsx)(v.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),f(e)},options:y,style:{width:"200px"}})]})]})}),(0,a.jsxs)(V.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{})})}),(0,a.jsx)(z.Z,{children:m.map((e,l)=>(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,a.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(j.Z,{type:"text",value:d,onChange:e=>c(e.target.value),onKeyDown:e=>{"Enter"===e.key&&S()},placeholder:"Type your message..."}),(0,a.jsx)(p.Z,{onClick:S,className:"ml-2",children:"Send"})]})})]})})]})})})})},lr=s(33509),li=s(95781);let{Sider:lo}=lr.default,ld=["Admin","Admin Viewer","Internal User","Internal Viewer"];var lc=e=>{let{setPage:l,userRole:s,defaultSelectedKey:t}=e;return"Admin Viewer"==s?(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,a.jsx)(lo,{width:120,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["4"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9")]})})}):(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,a.jsx)(lo,{width:145,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["1"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("api-keys"),children:(0,a.jsx)(_.Z,{children:"Virtual Keys"})},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("llm-playground"),children:(0,a.jsx)(_.Z,{children:"Test Key"})},"3"),"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("models"),children:(0,a.jsx)(_.Z,{children:"Models"})},"2"):null,ld.includes(s)?(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:(0,a.jsx)(_.Z,{children:"Usage"})},"4"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("users"),children:(0,a.jsx)(_.Z,{children:"Internal Users"})},"5"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("settings"),children:(0,a.jsx)(_.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("budgets"),children:(0,a.jsx)(_.Z,{children:"Budgets"})},"10"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("general-settings"),children:(0,a.jsx)(_.Z,{children:"Router Settings"})},"11"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("pass-through-settings"),children:(0,a.jsx)(_.Z,{children:"Pass-Through"})},"12"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("admin-panel"),children:(0,a.jsx)(_.Z,{children:"Admin Settings"})},"13"):null,(0,a.jsx)(li.Z.Item,{onClick:()=>l("api_ref"),children:(0,a.jsx)(_.Z,{children:"API Reference"})},"14"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("model-hub"),children:(0,a.jsx)(_.Z,{children:"Model Hub"})},"16")]})})})},lm=s(67989),lu=s(52703);console.log("process.env.NODE_ENV","production"),console.log=function(){};let lh=e=>null!==e&&("Admin"===e||"Admin Viewer"===e);var lx=e=>{let{accessToken:l,token:s,userRole:t,userID:n,keys:i,premiumUser:o}=e,d=new Date,[c,m]=(0,r.useState)([]),[j,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)([]),[b,v]=(0,r.useState)([]),[k,S]=(0,r.useState)([]),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)([]),[C,P]=(0,r.useState)([]),[T,E]=(0,r.useState)([]),[O,R]=(0,r.useState)([]),[F,M]=(0,r.useState)({}),[D,U]=(0,r.useState)([]),[J,Y]=(0,r.useState)(""),[$,Q]=(0,r.useState)(["all-tags"]),[ee,el]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),es=new Date(d.getFullYear(),d.getMonth(),1),et=new Date(d.getFullYear(),d.getMonth()+1,0),ep=e_(es),ej=e_(et);function eg(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}console.log("keys in usage",i),console.log("premium user in usage",o),(0,r.useEffect)(()=>{ef(ee.from,ee.to)},[ee,$]);let eZ=async(e,s,t)=>{if(!e||!s||!l)return;s.setHours(23,59,59,999),e.setHours(0,0,0,0),console.log("uiSelectedKey",t);let n=await (0,u.b1)(l,t,e.toISOString(),s.toISOString());console.log("End user data updated successfully",n),v(n)},ef=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),N((await (0,u.J$)(l,e.toISOString(),s.toISOString(),0===$.length?void 0:$)).spend_per_tag),console.log("Tag spend data updated successfully"))};function e_(e){let l=e.getFullYear(),s=e.getMonth()+1,t=e.getDate();return"".concat(l,"-").concat(s<10?"0"+s:s,"-").concat(t<10?"0"+t:t)}console.log("Start date is ".concat(ep)),console.log("End date is ".concat(ej));let ey=async(e,l,s)=>{try{let s=await e();l(s)}catch(e){console.error(s,e)}},eb=()=>ey(()=>l?(0,u.FC)(l):Promise.reject("No access token"),m,"Error fetching overall spend"),ev=()=>ey(()=>l&&s?(0,u.OU)(l,s,ep,ej):Promise.reject("No access token or token"),R,"Error fetching provider spend"),ek=async()=>{l&&await ey(async()=>(await (0,u.tN)(l)).map(e=>({key:(e.key_alias||e.key_name||e.api_key).substring(0,10),spend:e.total_spend})),g,"Error fetching top keys")},eS=async()=>{l&&await ey(async()=>(await (0,u.Au)(l)).map(e=>({key:e.model,spend:e.total_spend})),f,"Error fetching top models")},ew=async()=>{l&&await ey(async()=>{let e=await (0,u.mR)(l);return S(e.daily_spend),P(e.teams),e.total_spend_per_team.map(e=>({name:e.team_id||"",value:(e.total_spend||0).toFixed(2)}))},E,"Error fetching team spend")},eN=()=>{l&&ey(async()=>(await (0,u.X)(l)).tag_names,A,"Error fetching tag names")},eI=()=>{l&&ey(()=>{var e,s;return(0,u.J$)(l,null===(e=ee.from)||void 0===e?void 0:e.toISOString(),null===(s=ee.to)||void 0===s?void 0:s.toISOString(),void 0)},e=>N(e.spend_per_tag),"Error fetching top tags")},eA=()=>{l&&ey(()=>(0,u.b1)(l,null,void 0,void 0),v,"Error fetching top end users")},eC=()=>{l&&ey(()=>(0,u.wd)(l,ep,ej),M,"Error fetching global activity")},eP=()=>{l&&ey(()=>(0,u.xA)(l,ep,ej),U,"Error fetching global activity per model")};return(0,r.useEffect)(()=>{l&&s&&t&&n&&(eb(),ev(),ek(),eS(),eC(),eP(),lh(t)&&(ew(),eN(),eI(),eA()))},[l,s,t,n,ep,ej]),(0,a.jsx)("div",{style:{width:"100%"},className:"p-8",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{className:"mt-2",children:[(0,a.jsx)(er.Z,{children:"All Up"}),lh(t)?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(er.Z,{children:"Team Based Usage"}),(0,a.jsx)(er.Z,{children:"Customer Usage"}),(0,a.jsx)(er.Z,{children:"Tag Based Usage"})]}):(0,a.jsx)(a.Fragment,{children:(0,a.jsx)("div",{})})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"solid",className:"mt-1",children:[(0,a.jsx)(er.Z,{children:"Cost"}),(0,a.jsx)(er.Z,{children:"Activity"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[100vh] w-full",children:[(0,a.jsx)(X,{userID:n,userRole:t,accessToken:l,userSpend:null,selectedTeam:null,userMaxBudget:null}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Monthly Spend"}),(0,a.jsx)(ex.Z,{data:c,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top API Keys"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:j,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top Models"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:Z,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"✨ Spend by Provider"}),o?(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(lu.Z,{className:"mt-4 h-40",variant:"pie",data:O,index:"provider",category:"spend"})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Provider"}),(0,a.jsx)(K.Z,{children:"Spend"})]})}),(0,a.jsx)(z.Z,{children:O.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.provider}),(0,a.jsx)(B.Z,{children:1e-5>parseFloat(e.spend.toFixed(2))?"less than 0.00":e.spend.toFixed(2)})]},e.provider))})]})})]})}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})]})]})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"All Up"}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(F.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["api_requests"],onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(F.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]}),o?(0,a.jsx)(a.Fragment,{children:D.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["total_tokens"],valueFormatter:eg,onValueChange:e=>console.log(e)})]})]})]},l))}):(0,a.jsx)(a.Fragment,{children:D&&D.length>0&&D.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Activity by Model"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see analytics for all models"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],valueFormatter:eg,categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]})]},l))})]})})]})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(h.Z,{numColSpan:2,children:[(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"Total Spend Per Team"}),(0,a.jsx)(lm.Z,{data:T})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Daily Spend Per Team"}),(0,a.jsx)(ex.Z,{className:"h-72",data:k,showLegend:!0,index:"date",categories:C,yAxisWidth:80,stack:!0})]})]}),(0,a.jsx)(h.Z,{numColSpan:2})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:["Customers of your LLM API calls. Tracked when a `user` param is passed in your LLM calls ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/users",target:"_blank",children:"docs here"})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:ee,onValueChange:e=>{el(e),eZ(e.from,e.to,null)}})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Key"}),(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{eZ(ee.from,ee.to,null)},children:"All Keys"},"all-keys"),null==i?void 0:i.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{eZ(ee.from,ee.to,e.token)},children:e.key_alias},l):null)]})]})]}),(0,a.jsx)(L.Z,{className:"mt-4",children:(0,a.jsxs)(V.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Customer"}),(0,a.jsx)(K.Z,{children:"Spend"}),(0,a.jsx)(K.Z,{children:"Total Events"})]})}),(0,a.jsx)(z.Z,{children:null==b?void 0:b.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.end_user}),(0,a.jsx)(B.Z,{children:null===(s=e.total_spend)||void 0===s?void 0:s.toFixed(4)}),(0,a.jsx)(B.Z,{children:e.total_count})]},l)})})]})})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(ea.Z,{className:"mb-4",enableSelect:!0,value:ee,onValueChange:e=>{el(e),ef(e.from,e.to)}})}),(0,a.jsx)(h.Z,{children:o?(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsx)(eu.Z,{value:String(e),children:e},e))]})}):(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsxs)(G.Z,{value:String(e),disabled:!0,children:["✨ ",e," (Enterprise only Feature)"]},e))]})})})]}),(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Spend Per Tag"}),(0,a.jsxs)(_.Z,{children:["Get Started Tracking cost per tag ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/cost_tracking",target:"_blank",children:"here"})]}),(0,a.jsx)(ex.Z,{className:"h-72",data:w,index:"name",categories:["spend"],colors:["blue"]})]})}),(0,a.jsx)(h.Z,{numColSpan:2})]})]})]})]})})};let lp=e=>{if(e)return e.toISOString().split("T")[0]};function lj(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}var lg=e=>{let{accessToken:l,token:s,userRole:t,userID:n,premiumUser:i}=e,[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[p,j]=(0,r.useState)([]),[g,Z]=(0,r.useState)([]),[f,_]=(0,r.useState)("0"),[y,b]=(0,r.useState)("0"),[v,k]=(0,r.useState)("0"),[S,w]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date});(0,r.useEffect)(()=>{l&&S&&(async()=>{Z(await (0,u.zg)(l,lp(S.from),lp(S.to)))})()},[l]);let N=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.api_key)&&void 0!==l?l:""}))),I=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.model)&&void 0!==l?l:""})));Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.call_type)&&void 0!==l?l:""})));let A=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),Z(await (0,u.zg)(l,lp(e),lp(s))))};return(0,r.useEffect)(()=>{console.log("DATA IN CACHE DASHBOARD",g);let e=g;c.length>0&&(e=e.filter(e=>c.includes(e.api_key))),p.length>0&&(e=e.filter(e=>p.includes(e.model))),console.log("before processed data in cache dashboard",e);let l=0,s=0,t=0,n=e.reduce((e,n)=>{console.log("Processing item:",n),n.call_type||(console.log("Item has no call_type:",n),n.call_type="Unknown"),l+=(n.total_rows||0)-(n.cache_hit_true_rows||0),s+=n.cache_hit_true_rows||0,t+=n.cached_completion_tokens||0;let a=e.find(e=>e.name===n.call_type);return a?(a["LLM API requests"]+=(n.total_rows||0)-(n.cache_hit_true_rows||0),a["Cache hit"]+=n.cache_hit_true_rows||0,a["Cached Completion Tokens"]+=n.cached_completion_tokens||0,a["Generated Completion Tokens"]+=n.generated_completion_tokens||0):e.push({name:n.call_type,"LLM API requests":(n.total_rows||0)-(n.cache_hit_true_rows||0),"Cache hit":n.cache_hit_true_rows||0,"Cached Completion Tokens":n.cached_completion_tokens||0,"Generated Completion Tokens":n.generated_completion_tokens||0}),e},[]);_(lj(s)),b(lj(t));let a=s+l;a>0?k((s/a*100).toFixed(2)):k("0"),d(n),console.log("PROCESSED DATA IN CACHE DASHBOARD",n)},[c,p,S,g]),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(x.Z,{numItems:3,className:"gap-4 mt-4",children:[(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select API Keys",value:c,onValueChange:m,children:N.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select Models",value:p,onValueChange:j,children:I.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(ea.Z,{enableSelect:!0,value:S,onValueChange:e=>{w(e),A(e.from,e.to)},selectPlaceholder:"Select date range"})})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-4",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hit Ratio"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsxs)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:[v,"%"]})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hits"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:f})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cached Tokens"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:y})})]})]}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cache Hits vs API Requests"}),(0,a.jsx)(ex.Z,{title:"Cache Hits vs API Requests",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["LLM API requests","Cache hit"],colors:["sky","teal"],yAxisWidth:48}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cached Completion Tokens vs Generated Completion Tokens"}),(0,a.jsx)(ex.Z,{className:"mt-6",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["Generated Completion Tokens","Cached Completion Tokens"],colors:["sky","teal"],yAxisWidth:48})]})},lZ=()=>{let{Title:e,Paragraph:l}=es.default,[s,t]=(0,r.useState)(""),[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)(null),[h,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(null),[g,Z]=(0,r.useState)({PROXY_BASE_URL:"",PROXY_LOGOUT_URL:""}),[f,_]=(0,r.useState)(!0),y=(0,i.useSearchParams)(),[b,v]=(0,r.useState)({data:[]}),k=y.get("userID"),S=y.get("invitation_id"),w=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),[N,I]=(0,r.useState)("api-keys"),[A,C]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(w){let e=(0,el.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),C(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"internal_user":return"Internal User";case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),t(l),"Admin Viewer"==l&&I("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?_("username_password"==e.login_method):console.log("User Email is not set ".concat(e)),e.premium_user&&o(e.premium_user),e.auth_header_name&&(0,u.K8)(e.auth_header_name)}}},[w]),(0,a.jsx)(r.Suspense,{fallback:(0,a.jsx)("div",{children:"Loading..."}),children:S?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):(0,a.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,a.jsx)(m,{userID:k,userRole:s,userEmail:d,premiumUser:n,setProxySettings:Z,proxySettings:g}),(0,a.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,a.jsx)("div",{className:"mt-8",children:(0,a.jsx)(lc,{setPage:I,userRole:s,defaultSelectedKey:null})}),"api-keys"==N?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):"models"==N?(0,a.jsx)(eO,{userID:k,userRole:s,token:w,keys:p,accessToken:A,modelData:b,setModelData:v,premiumUser:n}):"llm-playground"==N?(0,a.jsx)(la,{userID:k,userRole:s,token:w,accessToken:A}):"users"==N?(0,a.jsx)(eL,{userID:k,userRole:s,token:w,keys:p,teams:h,accessToken:A,setKeys:j}):"teams"==N?(0,a.jsx)(eU,{teams:h,setTeams:x,searchParams:y,accessToken:A,userID:k,userRole:s}):"admin-panel"==N?(0,a.jsx)(eV,{setTeams:x,searchParams:y,accessToken:A,showSSOBanner:f,premiumUser:n}):"api_ref"==N?(0,a.jsx)(lt,{proxySettings:g}):"settings"==N?(0,a.jsx)(eJ,{userID:k,userRole:s,accessToken:A,premiumUser:n}):"budgets"==N?(0,a.jsx)(ll,{accessToken:A}):"general-settings"==N?(0,a.jsx)(e2,{userID:k,userRole:s,accessToken:A,modelData:b}):"model-hub"==N?(0,a.jsx)(ls.Z,{accessToken:A,publicPage:!1,premiumUser:n}):"caching"==N?(0,a.jsx)(lg,{userID:k,userRole:s,token:w,accessToken:A,premiumUser:n}):"pass-through-settings"==N?(0,a.jsx)(e7,{userID:k,userRole:s,accessToken:A,modelData:b}):(0,a.jsx)(lx,{userID:k,userRole:s,token:w,accessToken:A,keys:p,premiumUser:n})]})]})})}},41134:function(e,l,s){"use strict";s.d(l,{Z:function(){return y}});var t=s(57437),n=s(2265),a=s(47907),r=s(777),i=s(2179),o=s(13810),d=s(92836),c=s(26734),m=s(41608),u=s(32126),h=s(23682),x=s(71801),p=s(42440),j=s(84174),g=s(50459),Z=s(6180),f=s(99129),_=s(67951),y=e=>{var l;let{accessToken:s,publicPage:y,premiumUser:b}=e,[v,k]=(0,n.useState)(!1),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(!1),[A,C]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),E=(0,a.useRouter)();(0,n.useEffect)(()=>{s&&(async()=>{try{let e=await (0,r.kn)(s);console.log("ModelHubData:",e),w(e.data),(0,r.E9)(s,"enable_public_model_hub").then(e=>{console.log("data: ".concat(JSON.stringify(e))),!0==e.field_value&&k(!0)}).catch(e=>{})}catch(e){console.error("There was an error fetching the model data",e)}})()},[s,y]);let O=e=>{T(e),I(!0)},R=async()=>{s&&(0,r.jA)(s,"enable_public_model_hub",!0).then(e=>{C(!0)})},F=()=>{I(!1),C(!1),T(null)},M=()=>{I(!1),C(!1),T(null)},D=e=>{navigator.clipboard.writeText(e)};return(0,t.jsxs)("div",{children:[y&&v||!1==y?(0,t.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,t.jsx)("div",{className:"relative w-full"}),(0,t.jsxs)("div",{className:"flex ".concat(y?"justify-between":"items-center"),children:[(0,t.jsx)(p.Z,{className:"ml-8 text-center ",children:"Model Hub"}),!1==y?b?(0,t.jsx)(i.Z,{className:"ml-4",onClick:()=>R(),children:"✨ Make Public"}):(0,t.jsx)(i.Z,{className:"ml-4",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Make Public"})}):(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)("p",{children:"Filter by key:"}),(0,t.jsx)(x.Z,{className:"bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center",children:"/ui/model_hub?key="})]})]}),(0,t.jsx)("div",{className:"grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4 pr-8",children:S&&S.map(e=>(0,t.jsxs)(o.Z,{className:"mt-5 mx-8",children:[(0,t.jsxs)("pre",{className:"flex justify-between",children:[(0,t.jsx)(p.Z,{children:e.model_group}),(0,t.jsx)(Z.Z,{title:e.model_group,children:(0,t.jsx)(j.Z,{onClick:()=>D(e.model_group),style:{cursor:"pointer",marginRight:"10px"}})})]}),(0,t.jsxs)("div",{className:"my-5",children:[(0,t.jsxs)(x.Z,{children:["Mode: ",e.mode]}),(0,t.jsxs)(x.Z,{children:["Supports Function Calling:"," ",(null==e?void 0:e.supports_function_calling)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Supports Vision:"," ",(null==e?void 0:e.supports_vision)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Max Input Tokens:"," ",(null==e?void 0:e.max_input_tokens)?null==e?void 0:e.max_input_tokens:"N/A"]}),(0,t.jsxs)(x.Z,{children:["Max Output Tokens:"," ",(null==e?void 0:e.max_output_tokens)?null==e?void 0:e.max_output_tokens:"N/A"]})]}),(0,t.jsx)("div",{style:{marginTop:"auto",textAlign:"right"},children:(0,t.jsxs)("a",{href:"#",onClick:()=>O(e),style:{color:"#1890ff",fontSize:"smaller"},children:["View more ",(0,t.jsx)(g.Z,{})]})})]},e.model_group))})]}):(0,t.jsxs)(o.Z,{className:"mx-auto max-w-xl mt-10",children:[(0,t.jsx)(x.Z,{className:"text-xl text-center mb-2 text-black",children:"Public Model Hub not enabled."}),(0,t.jsx)("p",{className:"text-base text-center text-slate-800",children:"Ask your proxy admin to enable this on their Admin UI."})]}),(0,t.jsx)(f.Z,{title:"Public Model Hub",width:600,visible:A,footer:null,onOk:F,onCancel:M,children:(0,t.jsxs)("div",{className:"pt-5 pb-5",children:[(0,t.jsxs)("div",{className:"flex justify-between mb-4",children:[(0,t.jsx)(x.Z,{className:"text-base mr-2",children:"Shareable Link:"}),(0,t.jsx)(x.Z,{className:"max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded",children:"/ui/model_hub?key="})]}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(i.Z,{onClick:()=>{E.replace("/model_hub?key=".concat(s))},children:"See Page"})})]})}),(0,t.jsx)(f.Z,{title:P&&P.model_group?P.model_group:"Unknown Model",width:800,visible:N,footer:null,onOk:F,onCancel:M,children:P&&(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-4",children:(0,t.jsx)("strong",{children:"Model Information & Usage"})}),(0,t.jsxs)(c.Z,{children:[(0,t.jsxs)(m.Z,{children:[(0,t.jsx)(d.Z,{children:"OpenAI Python SDK"}),(0,t.jsx)(d.Z,{children:"Supported OpenAI Params"}),(0,t.jsx)(d.Z,{children:"LlamaIndex"}),(0,t.jsx)(d.Z,{children:"Langchain Py"})]}),(0,t.jsxs)(h.Z,{children:[(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="'.concat(P.model_group,'", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:"".concat(null===(l=P.supported_openai_params)||void 0===l?void 0:l.map(e=>"".concat(e,"\n")).join(""))})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="'.concat(P.model_group,'", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:4000",\n model = "'.concat(P.model_group,'",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})]})}}},function(e){e.O(0,[665,936,902,131,684,626,777,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/app/page-7c218fb97a2a9817.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-7c218fb97a2a9817.js deleted file mode 100644 index 5643948a48..0000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-7c218fb97a2a9817.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,s){Promise.resolve().then(s.bind(s,68031))},667:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return f}});var t=s(57437),n=s(2265),a=s(47907),r=s(2179),i=s(18190),o=s(13810),d=s(10384),c=s(46453),m=s(71801),u=s(52273),h=s(42440),x=s(30953),p=s(777),j=s(37963),g=s(60620),Z=s(13565);function f(){let[e]=g.Z.useForm(),l=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));l&&l.split("=")[1]}("token");let s=l.get("invitation_id"),[f,_]=(0,n.useState)(null),[y,b]=(0,n.useState)(""),[v,k]=(0,n.useState)(""),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(""),[A,C]=(0,n.useState)("");return(0,n.useEffect)(()=>{s&&(0,p.W_)(s).then(e=>{let l=e.login_url;console.log("login_url:",l),I(l);let s=e.token,t=(0,j.o)(s);C(s),console.log("decoded:",t),_(t.key),console.log("decoded user email:",t.user_email),k(t.user_email),w(t.user_id)})},[s]),(0,t.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,t.jsxs)(o.Z,{children:[(0,t.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,t.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,t.jsx)(m.Z,{children:"Claim your user account to login to Admin UI."}),(0,t.jsx)(i.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,t.jsxs)(c.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,t.jsx)(d.Z,{children:"SSO is under the Enterprise Tirer."}),(0,t.jsx)(d.Z,{children:(0,t.jsx)(r.Z,{variant:"primary",className:"mb-2",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,t.jsxs)(g.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",f,"token:",A,"formValues:",e),f&&A&&(e.user_email=v,S&&s&&(0,p.m_)(f,s,S,e.password).then(e=>{var l;let s="/ui/";s+="?userID="+((null===(l=e.data)||void 0===l?void 0:l.user_id)||e.user_id),document.cookie="token="+A,console.log("redirecting to:",s),window.location.href=s}))},children:[(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(g.Z.Item,{label:"Email Address",name:"user_email",children:(0,t.jsx)(u.Z,{type:"email",disabled:!0,value:v,defaultValue:v,className:"max-w-md"})}),(0,t.jsx)(g.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,t.jsx)(u.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,t.jsx)("div",{className:"mt-10",children:(0,t.jsx)(Z.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}},68031:function(e,l,s){"use strict";s.r(l),s.d(l,{default:function(){return lZ}});var t,n,a=s(57437),r=s(2265),i=s(47907),o=s(8792),d=s(40491),c=s(65270),m=e=>{let{userID:l,userRole:s,userEmail:t,premiumUser:n,setProxySettings:r,proxySettings:i}=e;console.log("User ID:",l),console.log("userEmail:",t),console.log("premiumUser:",n),console.log=function(){};let m="";console.log("PROXY_settings=",i),i&&i.PROXY_LOGOUT_URL&&void 0!==i.PROXY_LOGOUT_URL&&(m=i.PROXY_LOGOUT_URL),console.log("logoutUrl=",m);let u=[{key:"1",label:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{children:["Role: ",s]}),(0,a.jsxs)("p",{children:["ID: ",l]}),(0,a.jsxs)("p",{children:["Premium User: ",String(n)]})]})},{key:"2",label:(0,a.jsx)("p",{onClick:()=>{document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=m},children:"Logout"})}];return(0,a.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,a.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,a.jsx)("div",{className:"flex flex-col items-center",children:(0,a.jsx)(o.default,{href:"/",children:(0,a.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,a.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,a.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[n?null:(0,a.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Get enterprise license"})}),(0,a.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,a.jsx)(d.Z,{menu:{items:u},children:(0,a.jsx)(c.Z,{children:t})})})]})]})},u=s(777),h=s(10384),x=s(46453),p=s(2179),j=s(52273),g=s(26780),Z=s(15595),f=s(6698),_=s(71801),y=s(42440),b=s(42308),v=s(50670),k=s(60620),S=s(80588),w=s(99129),N=s(18559),I=s(44839),A=s(88707),C=s(13565);let{Option:P}=v.default;var T=e=>{let{userID:l,team:s,userRole:t,accessToken:n,data:i,setData:o}=e,[d]=k.Z.useForm(),[c,m]=(0,r.useState)(!1),[T,E]=(0,r.useState)(null),[O,R]=(0,r.useState)(null),[F,M]=(0,r.useState)([]),[D,L]=(0,r.useState)([]),[U,V]=(0,r.useState)("you"),z=()=>{m(!1),d.resetFields()},B=()=>{m(!1),E(null),d.resetFields()};(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==n){let e=(await (0,u.So)(n,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),M(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,t]);let q=async e=>{try{var s,t,a;let r=null!==(s=null==e?void 0:e.key_alias)&&void 0!==s?s:"",c=null!==(t=null==e?void 0:e.team_id)&&void 0!==t?t:null;if((null!==(a=null==i?void 0:i.filter(e=>e.team_id===c).map(e=>e.key_alias))&&void 0!==a?a:[]).includes(r))throw Error("Key alias ".concat(r," already exists for team with ID ").concat(c,", please provide another key alias"));if(S.ZP.info("Making API Call"),m(!0),"service_account"===U){let l={};try{l=JSON.parse(e.metadata||"{}")}catch(e){console.error("Error parsing metadata:",e)}l.service_account_id=e.key_alias,e.metadata=JSON.stringify(l)}let h=await (0,u.wX)(n,l,e);console.log("key create Response:",h),o(e=>e?[...e,h]:[h]),E(h.key),R(h.soft_budget),S.ZP.success("API Key Created"),d.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,r.useEffect)(()=>{L(s&&s.models.length>0?s.models.includes("all-proxy-models")?F:s.models:F)},[s,F]),(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,a.jsx)(w.Z,{title:"Create Key",visible:c,width:800,footer:null,onOk:z,onCancel:B,children:(0,a.jsxs)(k.Z,{form:d,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Owned By",className:"mb-4",children:(0,a.jsxs)(N.ZP.Group,{onChange:e=>V(e.target.value),value:U,children:[(0,a.jsx)(N.ZP,{value:"you",children:"You"}),(0,a.jsx)(N.ZP,{value:"service_account",children:"Service Account"})]})}),(0,a.jsx)(k.Z.Item,{label:"you"===U?"Key Name":"Service Account ID",name:"key_alias",rules:[{required:!0,message:"Please input a ".concat("you"===U?"key name":"service account ID")}],help:"you"===U?"required":"IDs can include letters, numbers, and hyphens",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:s?s.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s?s.team_alias:"",disabled:!0})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&d.setFieldsValue({models:["all-team-models"]})},children:[(0,a.jsx)(P,{value:"all-team-models",children:"All Team Models"},"all-team-models"),D.map(e=>(0,a.jsx)(P,{value:e,children:e},e))]})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==s?void 0:s.max_budget)!==null&&(null==s?void 0:s.max_budget)!==void 0?null==s?void 0:s.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.max_budget&&l>s.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(s.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==s?void 0:s.budget_duration)!==null&&(null==s?void 0:s.budget_duration)!==void 0?null==s?void 0:s.budget_duration:"None"),children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==s?void 0:s.tpm_limit)!==null&&(null==s?void 0:s.tpm_limit)!==void 0?null==s?void 0:s.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.tpm_limit&&l>s.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(s.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==s?void 0:s.rpm_limit)!==null&&(null==s?void 0:s.rpm_limit)!==void 0?null==s?void 0:s.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&s&&null!==s.rpm_limit&&l>s.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(s.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",className:"mt-8",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Key"})})]})}),T&&(0,a.jsx)(w.Z,{visible:c,onOk:z,onCancel:B,footer:null,children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Save your Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," ","through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:null!=T?(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:T})}),(0,a.jsx)(b.CopyToClipboard,{text:T,onCopy:()=>{S.ZP.success("API Key copied to clipboard")},children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,a.jsx)(_.Z,{children:"Key being created, this might take 30s"})})]})})]})},E=s(66002),O=s(9454),R=s(98941),F=s(63954),M=s(33393),D=s(5),L=s(13810),U=s(61244),V=s(10827),z=s(3851),B=s(2044),q=s(64167),K=s(74480),W=s(7178),H=s(95093),G=s(27166);let{Option:J}=v.default;console.log=function(){};var Y=e=>{let{userID:l,userRole:s,accessToken:t,selectedTeam:n,data:i,setData:o,teams:d,premiumUser:c}=e,[m,g]=(0,r.useState)(!1),[Z,f]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[P,T]=(0,r.useState)(null),[Y,X]=(0,r.useState)(null),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[es,et]=(0,r.useState)(!1),[en,ea]=(0,r.useState)(null),[er,ei]=(0,r.useState)([]),eo=new Set,[ed,ec]=(0,r.useState)(!1),[em,eu]=(0,r.useState)(!1),[eh,ex]=(0,r.useState)(null),[ep,ej]=(0,r.useState)(null),[eg]=k.Z.useForm(),[eZ,ef]=(0,r.useState)(null),[e_,ey]=(0,r.useState)(eo);(0,r.useEffect)(()=>{console.log("in calculateNewExpiryTime for selectedToken",en),(null==ep?void 0:ep.duration)?ef((e=>{if(!e)return null;try{let l;let s=new Date;if(e.endsWith("s"))l=(0,E.Z)(s,{seconds:parseInt(e)});else if(e.endsWith("h"))l=(0,E.Z)(s,{hours:parseInt(e)});else if(e.endsWith("d"))l=(0,E.Z)(s,{days:parseInt(e)});else throw Error("Invalid duration format");return l.toLocaleString("en-US",{year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",hour12:!0})}catch(e){return null}})(ep.duration)):ef(null),console.log("calculateNewExpiryTime:",eZ)},[en,null==ep?void 0:ep.duration]),(0,r.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==t&&null!==s){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),ei(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[t,l,s]);let eb=e=>{ea(e),ec(!0)},ev=async e=>{if(null==t||null==en)return;let l={...en,metadata:e,key:en.token};try{let e=await (0,u.Nc)(t,l);if(console.log("Model limits updated:",e),i){let l=i.map(l=>l.token===en.token?e:l);o(l)}S.ZP.success("Model-specific limits updated successfully")}catch(e){console.error("Error updating model-specific limits:",e),S.ZP.error("Failed to update model-specific limits")}ec(!1),ea(null)};(0,r.useEffect)(()=>{if(d){let e=new Set;d.forEach((l,s)=>{let t=l.team_id;e.add(t)}),ey(e)}},[d]);let ek=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id);let l=null;if(e.budget_duration)switch(e.budget_duration){case"24h":l="daily";break;case"7d":l="weekly";break;case"30d":l="monthly";break;default:l="None"}ea({...e,budget_duration:l}),el(!0)},eS=async e=>{if(null==t)return;let l=e.token;if(e.key=l,e.budget_duration)switch(e.budget_duration){case"daily":e.budget_duration="24h";break;case"weekly":e.budget_duration="7d";break;case"monthly":e.budget_duration="30d"}console.log("handleEditSubmit:",e);let s=await (0,u.Nc)(t,e);console.log("handleEditSubmit: newKeyValues",s),i&&o(i.map(e=>e.token===l?s:e)),S.ZP.success("Key updated successfully"),el(!1),ea(null)},ew=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=i&&(I(e.token),localStorage.removeItem("userData"+l),f(!0))},eN=async()=>{if(null!=N&&null!=i){try{await (0,u.I1)(t,N);let e=i.filter(e=>e.token!==N);o(e)}catch(e){console.error("Error deleting the key:",e)}f(!1),I(null)}},eI=e=>{ea(e),ef(null),eg.setFieldsValue({key_alias:e.key_alias,max_budget:e.max_budget,tpm_limit:e.tpm_limit,rpm_limit:e.rpm_limit,duration:e.duration||""}),eu(!0)},eA=(e,l)=>{ej(s=>({...s,[e]:l}))},eC=async()=>{if(!c){S.ZP.error("Regenerate API Key is an Enterprise feature. Please upgrade to use this feature.");return}if(null!=en)try{let e=await eg.validateFields(),l=await (0,u.s0)(t,en.token,e);if(ex(l.key),i){let s=i.map(s=>s.token===(null==en?void 0:en.token)?{...s,key_name:l.key_name,...e}:s);o(s)}eu(!1),eg.resetFields(),S.ZP.success("API Key regenerated successfully")}catch(e){console.error("Error regenerating key:",e),S.ZP.error("Failed to regenerate API Key")}};if(null!=i)return console.log("RERENDER TRIGGERED"),(0,a.jsxs)("div",{children:[(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,a.jsxs)(V.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Key Alias"}),(0,a.jsx)(K.Z,{children:"Secret Key"}),(0,a.jsx)(K.Z,{children:"Expires"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Budget Reset"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"Rate Limits"}),(0,a.jsx)(K.Z,{children:"Rate Limits per model"})]})}),(0,a.jsx)(z.Z,{children:i.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(n){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(e_.has(e.team_id),", selectedTeam id: ").concat(n.team_id)),(null!=n.team_id||null===e.team_id||e_.has(e.team_id))&&e.team_id!=n.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,a.jsx)(_.Z,{children:e.key_alias}):(0,a.jsx)(_.Z,{children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.key_name})}),(0,a.jsx)(B.Z,{children:null!=e.expires?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.expires).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,a.jsx)(B.Z,{children:null!=e.max_budget?(0,a.jsx)(_.Z,{children:e.max_budget}):(0,a.jsx)(_.Z,{children:"Unlimited"})}),(0,a.jsx)(B.Z,{children:null!=e.budget_reset_at?(0,a.jsx)("div",{children:(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:new Date(e.budget_reset_at).toLocaleString()})}):(0,a.jsx)("p",{style:{fontSize:"0.70rem"},children:"Never"})}),(0,a.jsx)(B.Z,{children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(a.Fragment,{children:n&&n.models&&n.models.length>0?n.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Team Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{size:"xs",onClick:()=>eb(e),children:"Edit Limits"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{onClick:()=>{ea(e),et(!0)},icon:O.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:es,onCancel:()=>{et(!1),ea(null)},footer:null,width:800,children:en&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-8",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(en.spend).toFixed(4)}catch(e){return en.spend}})()})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.max_budget?(0,a.jsxs)(a.Fragment,{children:[en.max_budget,en.budget_duration&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("br",{}),"Budget will be reset at ",en.budget_reset_at?new Date(en.budget_reset_at).toLocaleString():"Never"]})]}):(0,a.jsx)(a.Fragment,{children:"Unlimited"})})})]},e.name),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=en.expires?(0,a.jsx)(a.Fragment,{children:new Date(en.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,a.jsx)(a.Fragment,{children:"Never"})})})]},e.name)]}),(0,a.jsxs)(L.Z,{className:"my-4",children:[(0,a.jsx)(y.Z,{children:"Token Name"}),(0,a.jsx)(_.Z,{className:"my-1",children:en.key_alias?en.key_alias:en.key_name}),(0,a.jsx)(y.Z,{children:"Token ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.token}),(0,a.jsx)(y.Z,{children:"User ID"}),(0,a.jsx)(_.Z,{className:"my-1 text-[12px]",children:en.user_id}),(0,a.jsx)(y.Z,{children:"Metadata"}),(0,a.jsx)(_.Z,{className:"my-1",children:(0,a.jsxs)("pre",{children:[JSON.stringify(en.metadata)," "]})})]}),(0,a.jsx)(p.Z,{className:"mx-auto flex items-center",onClick:()=>{et(!1),ea(null)},children:"Close"})]})}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>ek(e)}),(0,a.jsx)(U.Z,{onClick:()=>eI(e),icon:F.Z,size:"sm"}),(0,a.jsx)(U.Z,{onClick:()=>ew(e),icon:M.Z,size:"sm"})]})]},e.token)})})]}),Z&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:eN,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{f(!1),I(null)},children:"Cancel"})]})]})]})})]}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:i}=e,[o]=k.Z.useForm(),[c,m]=(0,r.useState)(n),[u,h]=(0,r.useState)([]),[x,p]=(0,r.useState)(!1);return(0,a.jsx)(w.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{o.validateFields().then(e=>{o.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:o,onFinish:eS,initialValues:{...t,budget_duration:t.budget_duration},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let s=l.filter(e=>!c.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!c.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(s)),s.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(s,"Team models: ").concat(c.models)):Promise.resolve()}}],children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(J,{value:"all-team-models",children:"All Team Models"},"all-team-models"),c&&c.models?c.models.includes("all-proxy-models")?er.filter(e=>"all-proxy-models"!==e).map(e=>(0,a.jsx)(J,{value:e,children:e},e)):c.models.map(e=>(0,a.jsx)(J,{value:e,children:e},e)):er.map(e=>(0,a.jsx)(J,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==c?void 0:c.max_budget)!==null&&(null==c?void 0:c.max_budget)!==void 0?null==c?void 0:c.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.max_budget&&l>c.max_budget)throw console.log("keyTeam.max_budget: ".concat(c.max_budget)),Error("Budget cannot exceed team max budget: $".concat(c.max_budget))}}],children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Current Reset Budget: ".concat(t.budget_duration,", budget will be reset: ").concat(t.budget_reset_at?new Date(t.budget_reset_at).toLocaleString():"Never"),children:(0,a.jsxs)(v.default,{placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"daily",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"weekly",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"monthly",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"token",name:"token",hidden:!0}),(0,a.jsx)(k.Z.Item,{label:"Team",name:"team_id",className:"mt-8",help:"the team this key belongs to",children:(0,a.jsx)(H.Z,{value:t.team_alias,children:null==d?void 0:d.map((e,l)=>(0,a.jsx)(G.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"TPM Limit (tokens per minute)",name:"tpm_limit",help:"tpm_limit cannot exceed team tpm_limit ".concat((null==c?void 0:c.tpm_limit)!==null&&(null==c?void 0:c.tpm_limit)!==void 0?null==c?void 0:c.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.tpm_limit&&l>c.tpm_limit)throw console.log("keyTeam.tpm_limit: ".concat(c.tpm_limit)),Error("tpm_limit cannot exceed team max tpm_limit: $".concat(c.tpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"RPM Limit (requests per minute)",name:"rpm_limit",help:"rpm_limit cannot exceed team max tpm_limit: ".concat((null==c?void 0:c.rpm_limit)!==null&&(null==c?void 0:c.rpm_limit)!==void 0?null==c?void 0:c.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&c&&null!==c.rpm_limit&&l>c.rpm_limit)throw console.log("keyTeam.rpm_limit: ".concat(c.rpm_limit)),Error("rpm_limit cannot exceed team max rpm_limit: $".concat(c.rpm_limit))}}],children:(0,a.jsx)(A.Z,{step:1,precision:1,width:200})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:ee,onCancel:()=>{el(!1),ea(null)},token:en,onSubmit:eS}),en&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,token:t,onSubmit:n,accessToken:i}=e,[o,d]=(0,r.useState)({}),[c,m]=(0,r.useState)([]),[h,x]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(t.metadata){let e=t.metadata.model_tpm_limit||{},l=t.metadata.model_rpm_limit||{},s={};Object.keys({...e,...l}).forEach(t=>{s[t]={tpm:e[t]||0,rpm:l[t]||0}}),d(s)}(async()=>{try{let e=await (0,u.AZ)(i,"",""),l=Array.from(new Set(e.data.map(e=>e.model_name)));m(l)}catch(e){console.error("Error fetching model data:",e),S.ZP.error("Failed to fetch available models")}})()},[t,i]);let j=(e,l,s)=>{d(t=>({...t,[e]:{...t[e],[l]:s||0}}))},g=e=>{d(l=>{let{[e]:s,...t}=l;return t})};return(0,a.jsxs)(w.Z,{title:"Edit Model-Specific Limits",visible:l,onCancel:s,footer:null,width:800,children:[(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model"}),(0,a.jsx)(K.Z,{children:"TPM Limit"}),(0,a.jsx)(K.Z,{children:"RPM Limit"}),(0,a.jsx)(K.Z,{children:"Actions"})]})}),(0,a.jsxs)(z.Z,{children:[Object.entries(o).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:l}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.tpm,onChange:e=>j(l,"tpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(A.Z,{value:s.rpm,onChange:e=>j(l,"rpm",e)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>g(l),children:"Remove"})})]},l)}),null!==h&&(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(v.default,{style:{width:200},placeholder:"Select a model",onChange:e=>{o[e]||d(l=>({...l,[e]:{tpm:0,rpm:0}})),x(null)},value:h||void 0,children:c.filter(e=>!o.hasOwnProperty(e)).map(e=>(0,a.jsx)(J,{value:e,children:e},e))})}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:"-"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>x(null),children:"Cancel"})})]})]})]}),(0,a.jsx)(p.Z,{onClick:()=>{x("")},disabled:null!==h,children:"Add Limit"})]}),(0,a.jsxs)("div",{className:"flex justify-end space-x-4 mt-6",children:[(0,a.jsx)(p.Z,{onClick:s,children:"Cancel"}),(0,a.jsx)(p.Z,{onClick:()=>{n({...t.metadata,model_tpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.tpm]})),model_rpm_limit:Object.fromEntries(Object.entries(o).map(e=>{let[l,s]=e;return[l,s.rpm]}))})},children:"Save"})]})]})},{visible:ed,onCancel:()=>ec(!1),token:en,onSubmit:ev,accessToken:t}),(0,a.jsx)(w.Z,{title:"Regenerate API Key",visible:em,onCancel:()=>{eu(!1),eg.resetFields()},footer:[(0,a.jsx)(p.Z,{onClick:()=>{eu(!1),eg.resetFields()},className:"mr-2",children:"Cancel"},"cancel"),(0,a.jsx)(p.Z,{onClick:eC,disabled:!c,children:c?"Regenerate":"Upgrade to Regenerate"},"regenerate")],children:c?(0,a.jsxs)(k.Z,{form:eg,layout:"vertical",onValuesChange:(e,l)=>{"duration"in e&&eA("duration",e.duration)},children:[(0,a.jsx)(k.Z.Item,{name:"key_alias",label:"Key Alias",children:(0,a.jsx)(j.Z,{disabled:!0})}),(0,a.jsx)(k.Z.Item,{name:"max_budget",label:"Max Budget (USD)",children:(0,a.jsx)(A.Z,{step:.01,precision:2,style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"tpm_limit",label:"TPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"rpm_limit",label:"RPM Limit",children:(0,a.jsx)(A.Z,{style:{width:"100%"}})}),(0,a.jsx)(k.Z.Item,{name:"duration",label:"Expire Key (eg: 30s, 30h, 30d)",className:"mt-8",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsxs)("div",{className:"mt-2 text-sm text-gray-500",children:["Current expiry: ",(null==en?void 0:en.expires)!=null?new Date(en.expires).toLocaleString():"Never"]}),eZ&&(0,a.jsxs)("div",{className:"mt-2 text-sm text-green-600",children:["New expiry: ",eZ]})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",children:"Get Free Trial"})})]})}),eh&&(0,a.jsx)(w.Z,{visible:!!eh,onCancel:()=>ex(null),footer:[(0,a.jsx)(p.Z,{onClick:()=>ex(null),children:"Close"},"close")],children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Regenerated Key"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Please replace your old key with the new key generated. For security reasons, ",(0,a.jsx)("b",{children:"you will not be able to view it again"})," through your LiteLLM account. If you lose this secret key, you will need to generate a new one."]})}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(_.Z,{className:"mt-3",children:"Key Alias:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:(null==en?void 0:en.key_alias)||"No alias set"})}),(0,a.jsx)(_.Z,{className:"mt-3",children:"New API Key:"}),(0,a.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,a.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:eh})}),(0,a.jsx)(b.CopyToClipboard,{text:eh,onCopy:()=>S.ZP.success("API Key copied to clipboard"),children:(0,a.jsx)(p.Z,{className:"mt-3",children:"Copy API Key"})})]})]})})]})};console.log=function(){};var X=e=>{let{userID:l,userRole:s,accessToken:t,userSpend:n,userMaxBudget:i,selectedTeam:o}=e;console.log("userSpend: ".concat(n));let[d,c]=(0,r.useState)(null!==n?n:0),[m,h]=(0,r.useState)(o?o.max_budget:null);(0,r.useEffect)(()=>{if(o){if("Default Team"===o.team_alias)h(i);else{let e=!1;if(o.team_memberships)for(let s of o.team_memberships)s.user_id===l&&"max_budget"in s.litellm_budget_table&&null!==s.litellm_budget_table.max_budget&&(h(s.litellm_budget_table.max_budget),e=!0);e||h(o.max_budget)}}},[o,i]);let[x,p]=(0,r.useState)([]);(0,r.useEffect)(()=>{let e=async()=>{if(!t||!l||!s)return};(async()=>{try{if(null===l||null===s)return;if(null!==t){let e=(await (0,u.So)(t,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),p(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,t,l]),(0,r.useEffect)(()=>{null!==n&&c(n)},[n]);let j=[];o&&o.models&&(j=o.models),j&&j.includes("all-proxy-models")?(console.log("user models:",x),j=x):j&&j.includes("all-team-models")?j=o.models:j&&0===j.length&&(j=x);let g=void 0!==d?d.toFixed(4):null;return console.log("spend in view user spend: ".concat(d)),(0,a.jsx)("div",{className:"flex items-center",children:(0,a.jsxs)("div",{className:"flex justify-between gap-x-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Total Spend"}),(0,a.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",g]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:"Max Budget"}),(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null!==m?"$".concat(m," limit"):"No limit"})]})]})})};console.log=function(){};var $=e=>{let{userID:l,userRole:s,selectedTeam:t,accessToken:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==n){let e=(await (0,u.So)(n,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),o(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[n,l,s]);let d=[];return t&&t.models&&(d=t.models),d&&d.includes("all-proxy-models")&&(console.log("user models:",i),d=i),(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==t?void 0:t.team_alias}),(null==t?void 0:t.team_id)&&(0,a.jsxs)("p",{className:"text-xs text-gray-400 dark:text-gray-400 font-semibold",children:["Team ID: ",null==t?void 0:t.team_id]})]})})},Q=e=>{let l,{teams:s,setSelectedTeam:t,userRole:n,proxySettings:i,setProxySettings:o,userInfo:d,accessToken:c}=e;console.log("userInfo: ".concat(JSON.stringify(d)));let m={models:(null==d?void 0:d.models)||[],team_id:null,team_alias:"Default Team",max_budget:(null==d?void 0:d.max_budget)||null},h=async()=>{null===i&&c&&o(await (0,u.Dj)(c))};(0,r.useEffect)(()=>{h()},[i]);let[x,p]=(0,r.useState)(m);return console.log("userRole: ".concat(n)),console.log("proxySettings: ".concat(JSON.stringify(i))),l="App User"===n?s:i&&!0===i.DEFAULT_TEAM_DISABLED?s?[...s]:[m]:s?[...s,m]:[m],(0,a.jsxs)("div",{className:"mt-5 mb-5",children:[(0,a.jsx)(y.Z,{children:"Select Team"}),(0,a.jsx)(_.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,a.jsxs)(_.Z,{className:"mt-3 mb-3",children:[(0,a.jsx)("b",{children:"Default Team:"})," If no team_id is set for a key, it will be grouped under here."]}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>t(e),children:e.team_alias},l))}):(0,a.jsxs)(_.Z,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]})},ee=s(667),el=s(37963),es=s(97482);console.log=function(){},console.log("isLocal:",!1);var et=e=>{let{userID:l,userRole:s,teams:t,keys:n,setUserRole:o,userEmail:d,setUserEmail:c,setTeams:m,setKeys:p,premiumUser:j}=e,[g,Z]=(0,r.useState)(null),f=(0,i.useSearchParams)();f.get("viewSpend"),(0,i.useRouter)();let _=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),y=f.get("invitation_id"),[b,v]=(0,r.useState)(null),[k,S]=(0,r.useState)(null),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)(null),C={models:[],team_alias:"Default Team",team_id:null},[P,E]=(0,r.useState)(t?t[0]:C);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,r.useEffect)(()=>{if(_){let e=(0,el.o)(_);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),v(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"app_user":return"App User";case"internal_user":return"Internal User";case"internal_user_viewer":return"Internal Viewer";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),o(l)}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&b&&s&&!n&&!g){let e=sessionStorage.getItem("userModels"+l);e?N(JSON.parse(e)):(async()=>{try{let e=await (0,u.Dj)(b);A(e);let t=await (0,u.Br)(b,l,s,!1,null,null);console.log("received teams in user dashboard: ".concat(Object.keys(t),"; team values: ").concat(Object.entries(t.teams))),Z(t.user_info),console.log("userSpendData: ".concat(JSON.stringify(g))),p(t.keys),m(t.teams);let n=[...t.teams];n.length>0?(console.log("response['teams']: ".concat(n)),E(n[0])):E(C),sessionStorage.setItem("userData"+l,JSON.stringify(t.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(t.user_info));let a=(await (0,u.So)(b,l,s)).data.map(e=>e.id);console.log("available_model_names:",a),N(a),console.log("userModels:",w),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,_,b,n,s]),(0,r.useEffect)(()=>{if(null!==n&&null!=P&&null!==P.team_id){let e=0;for(let l of n)P.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===P.team_id&&(e+=l.spend);S(e)}else if(null!==n){let e=0;for(let l of n)e+=l.spend;S(e)}},[P]),null!=y)return(0,a.jsx)(ee.default,{});if(null==l||null==_){let e="/sso/key/generate";return document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",console.log("Full URL:",e),window.location.href=e,null}if(null==b)return null;if(null==s&&o("App Owner"),s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",P),(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)($,{userID:l,userRole:s,selectedTeam:P||null,accessToken:b}),(0,a.jsx)(X,{userID:l,userRole:s,userMaxBudget:(null==g?void 0:g.max_budget)||null,accessToken:b,userSpend:k,selectedTeam:P||null}),(0,a.jsx)(Y,{userID:l,userRole:s,accessToken:b,selectedTeam:P||null,data:n,setData:p,premiumUser:j,teams:t}),(0,a.jsx)(T,{userID:l,team:P||null,userRole:s,accessToken:b,data:n,setData:p},P?P.team_id:null),(0,a.jsx)(Q,{teams:t,setSelectedTeam:E,userRole:s,proxySettings:I,setProxySettings:A,userInfo:g,accessToken:b})]})})})},en=s(49167),ea=s(35087),er=s(92836),ei=s(26734),eo=s(41608),ed=s(32126),ec=s(23682),em=s(47047),eu=s(76628),eh=s(25707),ex=s(44041),ep=s(6180),ej=s(28683),eg=s(38302),eZ=s(66242),ef=s(78578),e_=s(34658),ey=e=>{let{modelID:l,accessToken:s}=e,[t,n]=(0,r.useState)(!1),i=async()=>{try{S.ZP.info("Making API Call"),n(!0);let e=await (0,u.Og)(s,l);console.log("model delete Response:",e),S.ZP.success("Model ".concat(l," deleted successfully")),n(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(U.Z,{onClick:()=>n(!0),icon:M.Z,size:"sm"}),(0,a.jsx)(w.Z,{open:t,onOk:i,okType:"danger",onCancel:()=>n(!1),children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full",children:[(0,a.jsx)(y.Z,{children:"Delete Model"}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("p",{children:["Model ID: ",(0,a.jsx)("b",{children:l})]})})]})})]})},eb=s(97766),ev=s(46495),ek=s(18190),eS=s(91118),ew=e=>{let{modelMetrics:l,modelMetricsCategories:s,customTooltip:t,premiumUser:n}=e;return n?(0,a.jsx)(eS.Z,{title:"Time to First token (s)",className:"h-72",data:l,index:"date",showLegend:!1,categories:s,colors:["indigo","rose"],connectNulls:!0,customTooltip:t}):(0,a.jsxs)("div",{children:[(0,a.jsx)(ek.Z,{title:"✨ Enterprise Feature",color:"teal",className:"mt-2 mb-4",children:"Enterprise features are available for users with a specific license, please contact LiteLLM to unlock this limitation."}),(0,a.jsx)(p.Z,{variant:"primary",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get in touch"})})]})},eN=e=>{let{fields:l,selectedProvider:s}=e;return 0===l.length?null:(0,a.jsx)(a.Fragment,{children:l.map(e=>(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:e.field_name.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase()),name:e.field_name,tooltip:e.field_description,className:"mb-2",children:(0,a.jsx)(j.Z,{placeholder:e.field_value,type:"password"})},e.field_name))})},eI=s(67951);let{Title:eA,Link:eC}=es.default;(t=n||(n={})).OpenAI="OpenAI",t.Azure="Azure",t.Azure_AI_Studio="Azure AI Studio",t.Anthropic="Anthropic",t.Google_AI_Studio="Google AI Studio",t.Bedrock="Amazon Bedrock",t.Groq="Groq",t.MistralAI="Mistral AI",t.Deepseek="Deepseek",t.OpenAI_Compatible="OpenAI-Compatible Endpoints (Together AI, etc.)",t.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)",t.Cohere="Cohere",t.Databricks="Databricks",t.Ollama="Ollama";let eP={OpenAI:"openai",Azure:"azure",Azure_AI_Studio:"azure_ai",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",Groq:"groq",MistralAI:"mistral",Cohere:"cohere_chat",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai",Databricks:"databricks",Deepseek:"deepseek",Ollama:"ollama"},eT={"BadRequestError (400)":"BadRequestErrorRetries","AuthenticationError (401)":"AuthenticationErrorRetries","TimeoutError (408)":"TimeoutErrorRetries","RateLimitError (429)":"RateLimitErrorRetries","ContentPolicyViolationError (400)":"ContentPolicyViolationErrorRetries","InternalServerError (500)":"InternalServerErrorRetries"},eE=async(e,l,s)=>{try{let t=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(t)),console.log("received type of deployments: ".concat(typeof t)),t.forEach(async s=>{console.log("litellm_model: ".concat(s));let t={},n={};t.model=s;let a="";for(let[l,s]of(console.log("formValues add deployment:",e),Object.entries(e)))if(""!==s){if("model_name"==l)a+=s;else if("custom_llm_provider"==l){console.log("custom_llm_provider:",s);let e=eP[s];t.custom_llm_provider=e,console.log("custom_llm_provider mappingResult:",e)}else if("model"==l)continue;else if("base_model"===l)n[l]=s;else if("custom_model_name"===l)t.model=s;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",s);let e={};if(s&&void 0!=s){try{e=JSON.parse(s)}catch(e){throw S.ZP.error("Failed to parse LiteLLM Extra Params: "+e,10),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,s]of Object.entries(e))t[l]=s}}else t[l]=s}let r={model_name:a,litellm_params:t,model_info:n},i=await (0,u.kK)(l,r);console.log("response for model create call: ".concat(i.data))}),s.resetFields()}catch(e){S.ZP.error("Failed to create model: "+e,10)}};var eO=e=>{let l,{accessToken:s,token:t,userRole:i,userID:o,modelData:d={data:[]},keys:c,setModelData:m,premiumUser:h}=e,[g,Z]=(0,r.useState)([]),[f]=k.Z.useForm(),[b,v]=(0,r.useState)(null),[N,I]=(0,r.useState)(""),[P,T]=(0,r.useState)([]),E=Object.values(n).filter(e=>isNaN(Number(e))),[M,J]=(0,r.useState)([]),[Y,X]=(0,r.useState)("OpenAI"),[$,Q]=(0,r.useState)(""),[ee,el]=(0,r.useState)(!1),[et,ek]=(0,r.useState)(!1),[eS,eO]=(0,r.useState)(null),[eR,eF]=(0,r.useState)([]),[eM,eD]=(0,r.useState)([]),[eL,eU]=(0,r.useState)(null),[eV,ez]=(0,r.useState)([]),[eB,eq]=(0,r.useState)([]),[eK,eW]=(0,r.useState)([]),[eH,eG]=(0,r.useState)([]),[eJ,eY]=(0,r.useState)([]),[eX,e$]=(0,r.useState)([]),[eQ,e0]=(0,r.useState)([]),[e1,e2]=(0,r.useState)([]),[e4,e5]=(0,r.useState)([]),[e8,e3]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),[e6,e7]=(0,r.useState)(null),[e9,le]=(0,r.useState)(0),[ll,ls]=(0,r.useState)({}),[lt,ln]=(0,r.useState)([]),[la,lr]=(0,r.useState)(!1),[li,lo]=(0,r.useState)(null),[ld,lc]=(0,r.useState)(null),[lm,lu]=(0,r.useState)([]);(0,r.useEffect)(()=>{lS(eL,e8.from,e8.to)},[li,ld]);let lh=e=>{eO(e),el(!0)},lx=e=>{eO(e),ek(!0)},lp=async e=>{if(console.log("handleEditSubmit:",e),null==s)return;let l={},t=null;for(let[s,n]of(e.input_cost_per_million_tokens&&(e.input_cost_per_token=e.input_cost_per_million_tokens/1e6,delete e.input_cost_per_million_tokens),e.output_cost_per_million_tokens&&(e.output_cost_per_token=e.output_cost_per_million_tokens/1e6,delete e.output_cost_per_million_tokens),Object.entries(e)))"model_id"!==s?l[s]=n:t=n;let n={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",n);try{await (0,u.um)(s,n),S.ZP.success("Model updated successfully, restart server to see updates"),el(!1),eO(null)}catch(e){console.log("Error occurred")}},lj=()=>{I(new Date().toLocaleString())},lg=async()=>{if(!s){console.error("Access token is missing");return}console.log("new modelGroupRetryPolicy:",e6);try{await (0,u.K_)(s,{router_settings:{model_group_retry_policy:e6}}),S.ZP.success("Retry settings saved successfully")}catch(e){console.error("Failed to save retry settings:",e),S.ZP.error("Failed to save retry settings")}};if((0,r.useEffect)(()=>{if(!s||!t||!i||!o)return;let e=async()=>{try{var e,l,t,n,a,r,d,c,h,x,p,j;let g=await (0,u.hy)(s);J(g);let Z=await (0,u.AZ)(s,o,i);console.log("Model data response:",Z.data),m(Z);let f=new Set;for(let e=0;e0&&(y=_[_.length-1],console.log("_initial_model_group:",y)),console.log("selectedModelGroup:",eL);let b=await (0,u.o6)(s,o,i,y,null===(e=e8.from)||void 0===e?void 0:e.toISOString(),null===(l=e8.to)||void 0===l?void 0:l.toISOString(),null==li?void 0:li.token,ld);console.log("Model metrics response:",b),eq(b.data),eW(b.all_api_bases);let v=await (0,u.Rg)(s,y,null===(t=e8.from)||void 0===t?void 0:t.toISOString(),null===(n=e8.to)||void 0===n?void 0:n.toISOString());eG(v.data),eY(v.all_api_bases);let k=await (0,u.N8)(s,o,i,y,null===(a=e8.from)||void 0===a?void 0:a.toISOString(),null===(r=e8.to)||void 0===r?void 0:r.toISOString(),null==li?void 0:li.token,ld);console.log("Model exceptions response:",k),e$(k.data),e0(k.exception_types);let S=await (0,u.fP)(s,o,i,y,null===(d=e8.from)||void 0===d?void 0:d.toISOString(),null===(c=e8.to)||void 0===c?void 0:c.toISOString(),null==li?void 0:li.token,ld),w=await (0,u.n$)(s,null===(h=e8.from)||void 0===h?void 0:h.toISOString().split("T")[0],null===(x=e8.to)||void 0===x?void 0:x.toISOString().split("T")[0],y);ls(w);let N=await (0,u.v9)(s,null===(p=e8.from)||void 0===p?void 0:p.toISOString().split("T")[0],null===(j=e8.to)||void 0===j?void 0:j.toISOString().split("T")[0],y);ln(N),console.log("dailyExceptions:",w),console.log("dailyExceptionsPerDeplyment:",N),console.log("slowResponses:",S),e5(S);let I=await (0,u.j2)(s);lu(null==I?void 0:I.end_users);let A=(await (0,u.BL)(s,o,i)).router_settings;console.log("routerSettingsInfo:",A);let C=A.model_group_retry_policy,P=A.num_retries;console.log("model_group_retry_policy:",C),console.log("default_retries:",P),e7(C),le(P)}catch(e){console.error("There was an error fetching the model data",e)}};s&&t&&i&&o&&e();let l=async()=>{let e=await (0,u.qm)(s);console.log("received model cost map data: ".concat(Object.keys(e))),v(e)};null==b&&l(),lj()},[s,t,i,o,b,N]),!d||!s||!t||!i||!o)return(0,a.jsx)("div",{children:"Loading..."});let lZ=[],lf=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(b)),null!=b&&"object"==typeof b&&e in b)?b[e].litellm_provider:"openai";if(s){let e=s.split("/"),l=e[0];(a=t)||(a=1===e.length?u(s):l)}else a="-";n&&(r=null==n?void 0:n.input_cost_per_token,i=null==n?void 0:n.output_cost_per_token,o=null==n?void 0:n.max_tokens,c=null==n?void 0:n.max_input_tokens),(null==l?void 0:l.litellm_params)&&(m=Object.fromEntries(Object.entries(null==l?void 0:l.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),d.data[e].provider=a,d.data[e].input_cost=r,d.data[e].output_cost=i,d.data[e].litellm_model_name=s,lf.push(a),d.data[e].input_cost&&(d.data[e].input_cost=(1e6*Number(d.data[e].input_cost)).toFixed(2)),d.data[e].output_cost&&(d.data[e].output_cost=(1e6*Number(d.data[e].output_cost)).toFixed(2)),d.data[e].max_tokens=o,d.data[e].max_input_tokens=c,d.data[e].api_base=null==l?void 0:null===(lb=l.litellm_params)||void 0===lb?void 0:lb.api_base,d.data[e].cleanedLitellmParams=m,lZ.push(l.model_name),console.log(d.data[e])}if(d.data&&d.data.length>0&&d.data.sort((e,l)=>e.provider&&l.provider?e.provider.localeCompare(l.provider):e.provider&&!l.provider?-1:!e.provider&&l.provider?1:0),i&&"Admin Viewer"==i){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let lv=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(n).find(l=>n[l]===e);if(l){let e=eP[l];console.log("mappingResult: ".concat(e));let s=[];"object"==typeof b&&(Object.entries(b).forEach(l=>{let[t,n]=l;null!==n&&"object"==typeof n&&"litellm_provider"in n&&(n.litellm_provider===e||n.litellm_provider.includes(e))&&s.push(t)}),"Cohere"==l&&(console.log("adding cohere chat model"),Object.entries(b).forEach(e=>{let[l,t]=e;null!==t&&"object"==typeof t&&"litellm_provider"in t&&"cohere"===t.litellm_provider&&s.push(l)}))),T(s),console.log("providerModels: ".concat(P))}},lk=async()=>{try{S.ZP.info("Running health check..."),Q("");let e=await (0,u.EY)(s);Q(e)}catch(e){console.error("Error running health check:",e),Q("Error running health check")}},lS=async(e,l,t)=>{if(console.log("Updating model metrics for group:",e),!s||!o||!i||!l||!t)return;console.log("inside updateModelMetrics - startTime:",l,"endTime:",t),eU(e);let n=null==li?void 0:li.token;void 0===n&&(n=null);let a=ld;void 0===a&&(a=null),l.setHours(0),l.setMinutes(0),l.setSeconds(0),t.setHours(23),t.setMinutes(59),t.setSeconds(59);try{let r=await (0,u.o6)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model metrics response:",r),eq(r.data),eW(r.all_api_bases);let d=await (0,u.Rg)(s,e,l.toISOString(),t.toISOString());eG(d.data),eY(d.all_api_bases);let c=await (0,u.N8)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);console.log("Model exceptions response:",c),e$(c.data),e0(c.exception_types);let m=await (0,u.fP)(s,o,i,e,l.toISOString(),t.toISOString(),n,a);if(console.log("slowResponses:",m),e5(m),e){let n=await (0,u.n$)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ls(n);let a=await (0,u.v9)(s,null==l?void 0:l.toISOString().split("T")[0],null==t?void 0:t.toISOString().split("T")[0],e);ln(a)}}catch(e){console.error("Failed to fetch model metrics",e)}},lw=(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Select API Key Name"}),h?(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{lo(e)},children:e.key_alias},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lc(e)},children:e},l))]})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{lo(null)},children:"All Keys"},"all-keys"),null==c?void 0:c.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsxs)(G.Z,{value:String(l),disabled:!0,onClick:()=>{lo(e)},children:["✨ ",e.key_alias," (Enterprise only Feature)"]},l):null)]}),(0,a.jsx)(_.Z,{className:"mt-1",children:"Select Customer Name"}),(0,a.jsxs)(H.Z,{defaultValue:"all-customers",children:[(0,a.jsx)(G.Z,{value:"all-customers",onClick:()=>{lc(null)},children:"All Customers"},"all-customers"),null==lm?void 0:lm.map((e,l)=>(0,a.jsxs)(G.Z,{value:e,disabled:!0,onClick:()=>{lc(e)},children:["✨ ",e," (Enterprise only Feature)"]},l))]})]})]}),lN=e=>{var l,s;let{payload:t,active:n}=e;if(!n||!t)return null;let r=null===(s=t[0])||void 0===s?void 0:null===(l=s.payload)||void 0===l?void 0:l.date,i=t.sort((e,l)=>l.value-e.value);if(i.length>5){let e=i.length-5;(i=i.slice(0,5)).push({dataKey:"".concat(e," other deployments"),value:t.slice(5).reduce((e,l)=>e+l.value,0),color:"gray"})}return(0,a.jsxs)("div",{className:"w-150 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown",children:[r&&(0,a.jsxs)("p",{className:"text-tremor-content-emphasis mb-2",children:["Date: ",r]}),i.map((e,l)=>{let s=parseFloat(e.value.toFixed(5)),t=0===s&&e.value>0?"<0.00001":s.toFixed(5);return(0,a.jsxs)("div",{className:"flex justify-between",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("div",{className:"w-2 h-2 mt-1 rounded-full bg-".concat(e.color,"-500")}),(0,a.jsx)("p",{className:"text-tremor-content",children:e.dataKey})]}),(0,a.jsx)("p",{className:"font-medium text-tremor-content-emphasis text-righ ml-2",children:t})]},l)})]})},lI=e=>"Vertex AI (Anthropic, Gemini, etc.)"===e?"gemini-pro":"Anthropic"==e||"Amazon Bedrock"==e?"claude-3-opus":"Google AI Studio"==e?"gemini-pro":"Azure AI Studio"==e?"azure_ai/command-r-plus":"Azure"==e?"azure/my-deployment":"gpt-3.5-turbo";console.log("selectedProvider: ".concat(Y)),console.log("providerModels.length: ".concat(P.length));let lA=Object.keys(n).find(e=>n[e]===Y);return lA&&(l=M.find(e=>e.name===eP[lA])),(0,a.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(er.Z,{children:"All Models"}),(0,a.jsx)(er.Z,{children:"Add Model"}),(0,a.jsx)(er.Z,{children:(0,a.jsx)("pre",{children:"/health Models"})}),(0,a.jsx)(er.Z,{children:"Model Analytics"}),(0,a.jsx)(er.Z,{children:"Model Retry Settings"})]}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[N&&(0,a.jsxs)(_.Z,{children:["Last Refreshed: ",N]}),(0,a.jsx)(U.Z,{icon:F.Z,variant:"shadow",size:"xs",className:"self-center",onClick:lj})]})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsxs)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||void 0,onValueChange:e=>eU("all"===e?"all":e),value:eL||void 0,children:[(0,a.jsx)(G.Z,{value:"all",children:"All Models"}),eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))]})]}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{style:{maxWidth:"1500px",width:"100%"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Public Model Name"}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Provider"}),(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"LiteLLM Model"}),"Admin"===i&&(0,a.jsx)(K.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"API Base"}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Input Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsxs)(K.Z,{style:{maxWidth:"85px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:["Output Price"," ",(0,a.jsx)("p",{style:{fontSize:"10px",color:"gray"},children:"/1M Tokens ($)"})]}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created At":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created At"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:h?"Created By":(0,a.jsxs)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",style:{color:"#72bcd4"},children:[" ","✨ Created By"]})}),(0,a.jsx)(K.Z,{style:{maxWidth:"50px",whiteSpace:"normal",wordBreak:"break-word",fontSize:"11px"},children:"Status"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:d.data.filter(e=>"all"===eL||e.model_name===eL||null==eL||""===eL).map((e,l)=>{var t;return(0,a.jsxs)(W.Z,{style:{maxHeight:"1px",minHeight:"1px"},children:[(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.model_name||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("p",{className:"text-xs",children:e.provider||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.litellm_model_name,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.litellm_model_name?e.litellm_model_name:"",children:e&&e.litellm_model_name?e.litellm_model_name.slice(0,20)+(e.litellm_model_name.length>20?"...":""):"-"})})}),"Admin"===i&&(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)(ep.Z,{title:e&&e.api_base,children:(0,a.jsx)("pre",{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},className:"text-xs",title:e&&e.api_base?e.api_base:"",children:e&&e.api_base?e.api_base.slice(0,20):"-"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.input_cost?e.input_cost:null!=e.litellm_params.input_cost_per_token&&void 0!=e.litellm_params.input_cost_per_token?(1e6*Number(e.litellm_params.input_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{style:{maxWidth:"80px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsx)("pre",{className:"text-xs",children:e.output_cost?e.output_cost:e.litellm_params.output_cost_per_token?(1e6*Number(e.litellm_params.output_cost_per_token)).toFixed(2):null})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&((t=e.model_info.created_at)?new Date(t).toLocaleDateString("en-US"):null)||"-"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)("p",{className:"text-xs",children:h&&e.model_info.created_by||"-"})}),(0,a.jsx)(B.Z,{style:{maxWidth:"100px",whiteSpace:"normal",wordBreak:"break-word"},children:e.model_info.db_model?(0,a.jsx)(D.Z,{size:"xs",className:"text-white",children:(0,a.jsx)("p",{className:"text-xs",children:"DB Model"})}):(0,a.jsx)(D.Z,{size:"xs",className:"text-black",children:(0,a.jsx)("p",{className:"text-xs",children:"Config Model"})})}),(0,a.jsx)(B.Z,{style:{maxWidth:"150px",whiteSpace:"normal",wordBreak:"break-word"},children:(0,a.jsxs)(x.Z,{numItems:3,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:O.Z,size:"sm",onClick:()=>lx(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>lh(e)})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(ey,{modelID:e.model_info.id,accessToken:s})})]})})]},l)})})]})})]}),(0,a.jsx)(e=>{let{visible:l,onCancel:s,model:t,onSubmit:n}=e,[r]=k.Z.useForm(),i={},o="",d="";if(t){i=t.litellm_params,o=t.model_name;let e=t.model_info;e&&(d=e.id,console.log("model_id: ".concat(d)),i.model_id=d)}return(0,a.jsx)(w.Z,{title:"Edit Model "+o,visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n(e),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:lp,initialValues:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"organization",name:"organization",tooltip:"OpenAI Organization ID",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"tpm",name:"tpm",tooltip:"int (optional) - Tokens limit for this deployment: in tokens per minute (tpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"rpm",name:"rpm",tooltip:"int (optional) - Rate limit for this deployment: in requests per minute (rpm). Find this information on your model/providers website",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"max_retries",name:"max_retries",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"Input Cost per 1M Tokens",name:"input_cost_per_million_tokens",tooltip:"float (optional) - Input cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"Output Cost per 1M Tokens",name:"output_cost_per_million_tokens",tooltip:"float (optional) - Output cost per 1 million tokens",children:(0,a.jsx)(A.Z,{min:0,step:.01})}),(0,a.jsx)(k.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:ee,onCancel:()=>{el(!1),eO(null)},model:eS,onSubmit:lp}),(0,a.jsxs)(w.Z,{title:eS&&eS.model_name,visible:et,width:800,footer:null,onCancel:()=>{ek(!1),eO(null)},children:[(0,a.jsx)(y.Z,{children:"Model Info"}),(0,a.jsx)(eI.Z,{language:"json",children:eS&&JSON.stringify(eS,null,2)})]})]}),(0,a.jsxs)(ed.Z,{className:"h-full",children:[(0,a.jsx)(eA,{level:2,children:"Add new model"}),(0,a.jsx)(L.Z,{children:(0,a.jsxs)(k.Z,{form:f,onFinish:()=>{f.validateFields().then(e=>{eE(e,s,f)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Provider:",name:"custom_llm_provider",tooltip:"E.g. OpenAI, Azure OpenAI, Anthropic, Bedrock, etc.",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:Y.toString(),children:E.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>{lv(e),X(e)},children:e},l))})}),(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Public Model Name",name:"model_name",tooltip:"Model name your users will pass in. Also used for load-balancing, LiteLLM will load balance between all models with this public name.",className:"mb-0",children:(0,a.jsx)(j.Z,{})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,a.jsxs)(k.Z.Item,{label:"LiteLLM Model Name(s)",tooltip:"Actual model name used for making litellm.completion() / litellm.embedding() call.",className:"mb-0",children:[(0,a.jsx)(k.Z.Item,{name:"model",rules:[{required:!0,message:"Required"}],noStyle:!0,children:"Azure"===Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"===Y||"Ollama"===Y?(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())}):P.length>0?(0,a.jsxs)(em.Z,{children:[(0,a.jsx)(eu.Z,{value:"custom",children:"Custom Model Name (Enter below)"}),P.map((e,l)=>(0,a.jsx)(eu.Z,{value:e,children:e},l))]}):(0,a.jsx)(j.Z,{placeholder:lI(Y.toString())})}),(0,a.jsx)(k.Z.Item,{noStyle:!0,shouldUpdate:(e,l)=>e.model!==l.model,children:e=>{let{getFieldValue:l}=e;return(l("model")||[]).includes("custom")&&(0,a.jsx)(k.Z.Item,{name:"custom_model_name",rules:[{required:!0,message:"Please enter a custom model name"}],className:"mt-2",children:(0,a.jsx)(j.Z,{placeholder:"Enter custom model name"})})}})]}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Actual model name used for making"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," ","models with the same 'public name'"]})})]}),void 0!==l&&l.fields.length>0&&(0,a.jsx)(eN,{fields:l.fields,selectedProvider:l.name}),"Amazon Bedrock"!=Y&&"Vertex AI (Anthropic, Gemini, etc.)"!=Y&&"Ollama"!=Y&&(void 0===l||0==l.fields.length)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,a.jsx)(j.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==Y&&(0,a.jsx)(k.Z.Item,{label:"Organization ID",name:"organization",children:(0,a.jsx)(j.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,a.jsx)(j.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,a.jsx)(ev.Z,{name:"file",accept:".json",beforeUpload:e=>{if("application/json"===e.type){let l=new FileReader;l.onload=e=>{if(e.target){let l=e.target.result;f.setFieldsValue({vertex_credentials:l})}},l.readAsText(e)}return!1},onChange(e){"uploading"!==e.file.status&&console.log(e.file,e.fileList),"done"===e.file.status?S.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&S.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,a.jsx)(C.ZP,{icon:(0,a.jsx)(eb.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==Y&&(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsx)(_.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==Y||"OpenAI-Compatible Endpoints (Together AI, etc.)"==Y)&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,a.jsx)(j.Z,{placeholder:"https://..."})}),"Azure"==Y&&(0,a.jsx)(k.Z.Item,{label:"API Version",name:"api_version",tooltip:"By default litellm will use the latest version. If you want to use a different version, you can specify it here",children:(0,a.jsx)(j.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==Y&&(0,a.jsxs)("div",{children:[(0,a.jsx)(k.Z.Item,{label:"Base Model",name:"base_model",className:"mb-0",children:(0,a.jsx)(j.Z,{placeholder:"azure/gpt-3.5-turbo"})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-2",children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from"," ",(0,a.jsx)(eC,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})})]})]}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Access Key ID",name:"aws_access_key_id",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Secret Access Key",name:"aws_secret_access_key",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:""})}),"Amazon Bedrock"==Y&&(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"AWS Region Name",name:"aws_region_name",tooltip:"You can provide the raw key or the environment variable (e.g. `os.environ/MY_SECRET_KEY`).",children:(0,a.jsx)(j.Z,{placeholder:"us-east-1"})}),(0,a.jsx)(k.Z.Item,{label:"LiteLLM Params",name:"litellm_extra_params",tooltip:"Optional litellm params used for making a litellm.completion() call.",className:"mb-0",children:(0,a.jsx)(ef.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,a.jsxs)(eg.Z,{children:[(0,a.jsx)(ej.Z,{span:10}),(0,a.jsx)(ej.Z,{span:10,children:(0,a.jsxs)(_.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params"," ",(0,a.jsx)(eC,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,a.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Model"})}),(0,a.jsx)(ep.Z,{title:"Get help on our github",children:(0,a.jsx)(es.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,a.jsx)(p.Z,{onClick:lk,children:"Run `/health`"}),$&&(0,a.jsx)("pre",{children:JSON.stringify($,null,2)})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:4,className:"mt-2 mb-2",children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:e8,className:"mr-2",onValueChange:e=>{e3(e),lS(eL,e.from,e.to)}})]}),(0,a.jsxs)(ej.Z,{className:"ml-2",children:[(0,a.jsx)(_.Z,{children:"Select Model Group"}),(0,a.jsx)(H.Z,{defaultValue:eL||eR[0],value:eL||eR[0],children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>lS(e,e8.from,e8.to),children:e},l))})]}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(eZ.Z,{trigger:"click",content:lw,overlayStyle:{width:"20vw"},children:(0,a.jsx)(p.Z,{icon:e_.Z,size:"md",variant:"secondary",className:"mt-4 ml-2",style:{border:"none"},onClick:()=>lr(!0)})})})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"mr-2 max-h-[400px] min-h-[400px]",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Avg. Latency per Token"}),(0,a.jsx)(er.Z,{value:"2",children:"✨ Time to first token"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("p",{className:"text-gray-500 italic",children:" (seconds/token)"}),(0,a.jsx)(_.Z,{className:"text-gray-500 italic mt-1 mb-1",children:"average Latency for successfull requests divided by the total tokens"}),eB&&eK&&(0,a.jsx)(eh.Z,{title:"Model Latency",className:"h-72",data:eB,showLegend:!1,index:"date",categories:eK,connectNulls:!0,customTooltip:lN})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(ew,{modelMetrics:eH,modelMetricsCategories:eJ,customTooltip:lN,premiumUser:h})})]})]})})}),(0,a.jsx)(ej.Z,{children:(0,a.jsx)(L.Z,{className:"ml-2 max-h-[400px] min-h-[400px] overflow-y-auto",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Deployment"}),(0,a.jsx)(K.Z,{children:"Success Responses"}),(0,a.jsxs)(K.Z,{children:["Slow Responses ",(0,a.jsx)("p",{children:"Success Responses taking 600+s"})]})]})}),(0,a.jsx)(z.Z,{children:e4.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.api_base}),(0,a.jsx)(B.Z,{children:e.total_count}),(0,a.jsx)(B.Z,{children:e.slow_count})]},l))})]})})})]}),(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Exceptions for ",eL]}),(0,a.jsx)(ex.Z,{className:"h-60",data:eX,index:"model",categories:eQ,stack:!0,yAxisWidth:30})]})}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 w-full mt-2",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(y.Z,{children:["All Up Rate Limit Errors (429) for ",eL]}),(0,a.jsxs)(x.Z,{numItems:1,children:[(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",ll.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:ll.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]}),(0,a.jsx)(ej.Z,{})]})]}),h?(0,a.jsx)(a.Fragment,{children:lt.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base?e.api_base:"Unknown API Base"}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors (429) ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]},l))}):(0,a.jsx)(a.Fragment,{children:lt&<.length>0&<.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Rate Limit Errors by Deployment"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see exceptions for all deployments"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.api_base}),(0,a.jsx)(x.Z,{numItems:1,children:(0,a.jsxs)(ej.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Num Rate Limit Errors ",e.sum_num_rate_limit_exceptions]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["rose"],categories:["num_rate_limit_exceptions"],onValueChange:e=>console.log(e)})]})})]})]},l))})]})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)(_.Z,{children:"Filter by Public Model Name"}),(0,a.jsx)(H.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:eL||eR[0],value:eL||eR[0],onValueChange:e=>eU(e),children:eR.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>eU(e),children:e},l))})]}),(0,a.jsxs)(y.Z,{children:["Retry Policy for ",eL]}),(0,a.jsx)(_.Z,{className:"mb-6",children:"How many retries should be attempted based on the Exception"}),eT&&(0,a.jsx)("table",{children:(0,a.jsx)("tbody",{children:Object.entries(eT).map((e,l)=>{var s;let[t,n]=e,r=null==e6?void 0:null===(s=e6[eL])||void 0===s?void 0:s[n];return null==r&&(r=e9),(0,a.jsxs)("tr",{className:"flex justify-between items-center mt-2",children:[(0,a.jsx)("td",{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)("td",{children:(0,a.jsx)(A.Z,{className:"ml-5",value:r,min:0,step:1,onChange:e=>{e7(l=>{var s;let t=null!==(s=null==l?void 0:l[eL])&&void 0!==s?s:{};return{...null!=l?l:{},[eL]:{...t,[n]:e}}})}})})]},l)})})}),(0,a.jsx)(p.Z,{className:"mt-6 mr-8",onClick:lg,children:"Save"})]})]})]})})},eR=e=>{let{isInvitationLinkModalVisible:l,setIsInvitationLinkModalVisible:s,baseUrl:t,invitationLinkData:n}=e,{Title:r,Paragraph:i}=es.default,o=()=>(null==n?void 0:n.has_user_setup_sso)?"".concat(t,"/ui"):"".concat(t,"/ui?invitation_id=").concat(null==n?void 0:n.id);return(0,a.jsxs)(w.Z,{title:"Invitation Link",visible:l,width:800,footer:null,onOk:()=>{s(!1)},onCancel:()=>{s(!1)},children:[(0,a.jsx)(i,{children:"Copy and send the generated link to onboard this user to the proxy."}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"User ID"}),(0,a.jsx)(_.Z,{children:null==n?void 0:n.user_id})]}),(0,a.jsxs)("div",{className:"flex justify-between pt-5 pb-2",children:[(0,a.jsx)(_.Z,{children:"Invitation Link"}),(0,a.jsx)(_.Z,{children:(0,a.jsx)(_.Z,{children:o()})})]}),(0,a.jsxs)("div",{className:"flex justify-end mt-5",children:[(0,a.jsx)("div",{}),(0,a.jsx)(b.CopyToClipboard,{text:o(),onCopy:()=>S.ZP.success("Copied!"),children:(0,a.jsx)(p.Z,{variant:"primary",children:"Copy invitation link"})})]})]})};let{Option:eF}=v.default;var eM=e=>{let{userID:l,accessToken:s,teams:t,possibleUIRoles:n}=e,[o,d]=(0,r.useState)(null),[c]=k.Z.useForm(),[m,h]=(0,r.useState)(!1),[x,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)([]),[y,b]=(0,r.useState)(!1),[N,A]=(0,r.useState)(null),P=(0,i.useRouter)();console.log=function(){};let[T,E]=(0,r.useState)("");(0,r.useEffect)(()=>{(async()=>{try{let e=await (0,u.So)(s,l,"any"),t=[];for(let l=0;l{if(P){let{protocol:e,host:l}=window.location;E("".concat(e,"/").concat(l))}},[P]);let O=async e=>{try{var t;S.ZP.info("Making API Call"),h(!0),console.log("formValues in create user:",e);let n=await (0,u.Ov)(s,null,e);console.log("user create Response:",n),g(n.key);let a=(null===(t=n.data)||void 0===t?void 0:t.user_id)||n.user_id;if(null==o?void 0:o.SSO_ENABLED){let e={id:crypto.randomUUID(),user_id:a,is_accepted:!1,accepted_at:null,expires_at:new Date(Date.now()+6048e5),created_at:new Date,created_by:l,updated_at:new Date,updated_by:l,has_user_setup_sso:!0};A(e),b(!0)}else(0,u.XO)(s,a).then(e=>{e.has_user_setup_sso=!1,A(e),b(!0)});S.ZP.success("API user Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-0",onClick:()=>h(!0),children:"+ Invite User"}),(0,a.jsxs)(w.Z,{title:"Invite User",visible:m,width:800,footer:null,onOk:()=>{h(!1),c.resetFields()},onCancel:()=>{h(!1),g(null),c.resetFields()},children:[(0,a.jsx)(_.Z,{className:"mb-1",children:"Create a User who can own keys"}),(0,a.jsxs)(k.Z,{form:c,onFinish:O,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(k.Z.Item,{label:"User Email",name:"user_email",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:n&&Object.entries(n).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"team_id",children:(0,a.jsx)(v.default,{placeholder:"Select Team ID",style:{width:"100%"},children:t?t.map(e=>(0,a.jsx)(eF,{value:e.team_id,children:e.team_alias},e.team_id)):(0,a.jsx)(eF,{value:null,children:"Default Team"},"default")})}),(0,a.jsx)(k.Z.Item,{label:"Metadata",name:"metadata",children:(0,a.jsx)(I.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create User"})})]})]}),x&&(0,a.jsx)(eR,{isInvitationLinkModalVisible:y,setIsInvitationLinkModalVisible:b,baseUrl:T,invitationLinkData:N})]})},eD=e=>{let{visible:l,possibleUIRoles:s,onCancel:t,user:n,onSubmit:i}=e,[o,d]=(0,r.useState)(n),[c]=k.Z.useForm();(0,r.useEffect)(()=>{c.resetFields()},[n]);let m=async()=>{c.resetFields(),t()},u=async e=>{i(e),c.resetFields(),t()};return n?(0,a.jsx)(w.Z,{visible:l,onCancel:m,footer:null,title:"Edit User "+n.user_id,width:1e3,children:(0,a.jsx)(k.Z,{form:c,onFinish:u,initialValues:n,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"User Email",tooltip:"Email of the User",name:"user_email",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"user_id",name:"user_id",hidden:!0,children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"User Role",name:"user_role",children:(0,a.jsx)(v.default,{children:s&&Object.entries(s).map(e=>{let[l,{ui_label:s,description:t}]=e;return(0,a.jsx)(G.Z,{value:l,title:s,children:(0,a.jsxs)("div",{className:"flex",children:[s," ",(0,a.jsx)("p",{className:"ml-2",style:{color:"gray",fontSize:"12px"},children:t})]})},l)})})}),(0,a.jsx)(k.Z.Item,{label:"Spend (USD)",name:"spend",tooltip:"(float) - Spend of all LLM calls completed by this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)(k.Z.Item,{label:"User Budget (USD)",name:"max_budget",tooltip:"(float) - Maximum budget of this user",children:(0,a.jsx)(A.Z,{min:0,step:1})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})}):null};console.log=function(){};var eL=e=>{let{accessToken:l,token:s,keys:t,userRole:n,userID:i,teams:o,setKeys:d}=e,[c,m]=(0,r.useState)(null),[h,p]=(0,r.useState)(null),[j,g]=(0,r.useState)(null),[Z,f]=(0,r.useState)(1),[_,y]=r.useState(null),[b,v]=(0,r.useState)(null),[k,w]=(0,r.useState)(!1),[N,I]=(0,r.useState)(null),[A,C]=(0,r.useState)({}),P=async()=>{I(null),w(!1)},T=async e=>{if(console.log("inside handleEditSubmit:",e),l&&s&&n&&i){try{await (0,u.pf)(l,e,null),S.ZP.success("User ".concat(e.user_id," updated successfully"))}catch(e){console.error("There was an error updating the user",e)}h&&p(h.map(l=>l.user_id===e.user_id?e:l)),I(null),w(!1)}};return((0,r.useEffect)(()=>{if(!l||!s||!n||!i)return;let e=async()=>{try{let e=await (0,u.Br)(l,null,n,!0,Z,25);m(e),console.log("user data response:",e),p(e.users||[]);let s=await (0,u.lg)(l);C(s)}catch(e){console.error("There was an error fetching the model data",e)}};l&&s&&n&&i&&e()},[l,s,n,i,Z]),h&&l&&s&&n&&i)?(0,a.jsx)("div",{style:{width:"100%"},children:(0,a.jsxs)(x.Z,{className:"gap-2 p-2 h-[90vh] w-full mt-8",children:[(0,a.jsx)(eM,{userID:i,accessToken:l,teams:o,possibleUIRoles:A}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[90vh] mb-4",children:[(0,a.jsx)("div",{className:"mb-4 mt-1"}),(0,a.jsx)(ei.Z,{children:(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(V.Z,{className:"mt-5",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"User ID"}),(0,a.jsx)(K.Z,{children:"User Email"}),(0,a.jsx)(K.Z,{children:"Role"}),(0,a.jsx)(K.Z,{children:"User Spend ($ USD)"}),(0,a.jsx)(K.Z,{children:"User Max Budget ($ USD)"}),(0,a.jsx)(K.Z,{children:"API Keys"}),(0,a.jsx)(K.Z,{})]})}),(0,a.jsx)(z.Z,{children:h.map(e=>{var l,s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_id||"-"}),(0,a.jsx)(B.Z,{children:e.user_email||"-"}),(0,a.jsx)(B.Z,{children:(null==A?void 0:null===(l=A[null==e?void 0:e.user_role])||void 0===l?void 0:l.ui_label)||"-"}),(0,a.jsx)(B.Z,{children:e.spend?null===(s=e.spend)||void 0===s?void 0:s.toFixed(2):"-"}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(x.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,a.jsxs)(D.Z,{size:"xs",color:"indigo",children:[e.key_aliases.filter(e=>null!==e).length,"\xa0Keys"]}):(0,a.jsx)(D.Z,{size:"xs",color:"gray",children:"No Keys"})})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:R.Z,onClick:()=>{I(e),w(!0)},children:"View Keys"})})]},e.user_id)})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"flex-1"}),(0,a.jsx)("div",{className:"flex-1 flex justify-between items-center"})]})})]})}),(0,a.jsx)(eD,{visible:k,possibleUIRoles:A,onCancel:P,user:N,onSubmit:T})]}),function(){if(!h)return null;let e=(null==c?void 0:c.total_pages)||0,l=e=>{p([]),f(e)};return(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsxs)("div",{children:["Showing Page ",Z," of ",e]}),(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:1===Z,onClick:()=>l(Z-1),children:"← Prev"}),(0,a.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",disabled:Z===e,onClick:()=>l(Z+1),children:"Next →"})]})]})}()]})}):(0,a.jsx)("div",{children:"Loading..."})};console.log=function(){};var eU=e=>{let{teams:l,searchParams:s,accessToken:t,setTeams:n,userID:i,userRole:o}=e;(0,r.useEffect)(()=>{console.log("inside useeffect - ".concat(l)),null===l&&t&&(async()=>{let e=await (0,u.It)(t);console.log("givenTeams: ".concat(e)),n(e)})()},[l]);let[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:g}=es.default,[Z,f]=(0,r.useState)(""),[y,b]=(0,r.useState)(!1),[N,P]=(0,r.useState)(l?l[0]:null),[T,E]=(0,r.useState)(!1),[O,F]=(0,r.useState)(!1),[J,Y]=(0,r.useState)([]),[X,$]=(0,r.useState)(!1),[Q,ee]=(0,r.useState)(null),[el,et]=(0,r.useState)({}),en=e=>{P(e),b(!0)},ea=async e=>{let s=e.team_id;if(console.log("handleEditSubmit:",e),null==t)return;let a=await (0,u.Gh)(t,e);l&&n(l.map(e=>e.team_id===s?a.data:e)),S.ZP.success("Team updated successfully"),b(!1),P(null)},er=async e=>{ee(e),$(!0)},ei=async()=>{if(null!=Q&&null!=l&&null!=t){try{await (0,u.rs)(t,Q);let e=l.filter(e=>e.team_id!==Q);n(e)}catch(e){console.error("Error deleting the team:",e)}$(!1),ee(null)}};(0,r.useEffect)(()=>{let e=async()=>{try{if(null===i||null===o||null===t||null===l)return;let e={},s=await (0,u.It)(t);for(let l=0;l{try{if(null===i||null===o)return;if(null!==t){let e=(await (0,u.So)(t,i,o)).data.map(e=>e.id);console.log("available_model_names:",e),Y(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,i,o,l]);let eo=async e=>{try{if(null!=t){var s;let a=null==e?void 0:e.team_alias;if((null!==(s=null==l?void 0:l.map(e=>e.team_alias))&&void 0!==s?s:[]).includes(a))throw Error("Team alias ".concat(a," already exists, please pick another alias"));S.ZP.info("Creating Team");let r=await (0,u.hT)(t,e);null!==l?n([...l,r]):n([r]),console.log("response for team create call: ".concat(r)),S.ZP.success("Team created"),E(!1)}}catch(e){console.error("Error creating the team:",e),S.ZP.error("Error creating the team: "+e,20)}},ed=async e=>{try{if(null!=t&&null!=l){S.ZP.info("Adding Member");let s={role:"user",user_email:e.user_email,user_id:e.user_id},a=await (0,u.cu)(t,N.team_id,s);console.log("response for team create call: ".concat(a.data));let r=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(a.data.team_id)),e.team_id===a.data.team_id));if(console.log("foundIndex: ".concat(r)),-1!==r){let e=[...l];e[r]=a.data,n(e),P(a.data)}F(!1)}}catch(e){console.error("Error creating the team:",e)}};return(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"All Teams"}),(0,a.jsxs)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Team Name"}),(0,a.jsx)(K.Z,{children:"Team ID"}),(0,a.jsx)(K.Z,{children:"Spend (USD)"}),(0,a.jsx)(K.Z,{children:"Budget (USD)"}),(0,a.jsx)(K.Z,{children:"Models"}),(0,a.jsx)(K.Z,{children:"TPM / RPM Limits"}),(0,a.jsx)(K.Z,{children:"Info"})]})}),(0,a.jsx)(z.Z,{children:l&&l.length>0?l.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",fontSize:"0.75em"},children:(0,a.jsx)(ep.Z,{title:e.team_id,children:e.team_id})}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!==e.max_budget&&void 0!==e.max_budget?e.max_budget:"No limit"}),(0,a.jsx)(B.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,a.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"red",children:(0,a.jsx)(_.Z,{children:"All Proxy Models"})},l):(0,a.jsx)(D.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,a.jsx)(_.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,a.jsx)(B.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,a.jsxs)(_.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,a.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].keys&&el[e.team_id].keys.length," ","Keys"]}),(0,a.jsxs)(_.Z,{children:[el&&e.team_id&&el[e.team_id]&&el[e.team_id].team_info&&el[e.team_id].team_info.members_with_roles&&el[e.team_id].team_info.members_with_roles.length," ","Members"]})]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>en(e)}),(0,a.jsx)(U.Z,{onClick:()=>er(e.team_id),icon:M.Z,size:"sm"})]})]},e.team_id)):null})]}),X&&(0,a.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,a.jsxs)("div",{className:"flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0",children:[(0,a.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,a.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,a.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,a.jsxs)("div",{className:"inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full",children:[(0,a.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,a.jsx)("div",{className:"sm:flex sm:items-start",children:(0,a.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,a.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,a.jsx)("div",{className:"mt-2",children:(0,a.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,a.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,a.jsx)(p.Z,{onClick:ei,color:"red",className:"ml-2",children:"Delete"}),(0,a.jsx)(p.Z,{onClick:()=>{$(!1),ee(null)},children:"Cancel"})]})]})]})})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>E(!0),children:"+ Create New Team"}),(0,a.jsx)(w.Z,{title:"Create Team",visible:T,width:800,footer:null,onOk:()=>{E(!1),d.resetFields()},onCancel:()=>{E(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:eo,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(m,{level:4,children:"Team Members"}),(0,a.jsx)(g,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,a.jsx)(H.Z,{defaultValue:"0",children:l.map((e,l)=>(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{P(e)},children:e.team_alias},l))}):(0,a.jsxs)(g,{children:["No team created. ",(0,a.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:N?N.members_with_roles.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsx)(B.Z,{children:e.role})]},l)):null})]})}),N&&(0,a.jsx)(e=>{let{visible:l,onCancel:s,team:t,onSubmit:n}=e,[r]=k.Z.useForm();return(0,a.jsx)(w.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{r.validateFields().then(e=>{n({...e,team_id:t.team_id}),r.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:s,children:(0,a.jsxs)(k.Z,{form:r,onFinish:ea,initialValues:t,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Models",name:"models",children:(0,a.jsxs)(v.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,a.jsx)(v.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),J&&J.map(e=>(0,a.jsx)(v.default.Option,{value:e,children:e},e))]})}),(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})}),(0,a.jsx)(k.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,a.jsx)(A.Z,{step:1,width:400})}),(0,a.jsx)(k.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:y,onCancel:()=>{b(!1),P(null)},team:N,onSubmit:ea})]}),(0,a.jsxs)(h.Z,{numColSpan:1,children:[(0,a.jsx)(p.Z,{className:"mx-auto mb-5",onClick:()=>F(!0),children:"+ Add member"}),(0,a.jsx)(w.Z,{title:"Add member",visible:O,width:800,footer:null,onOk:()=>{F(!1),c.resetFields()},onCancel:()=>{F(!1),c.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:ed,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,a.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,a.jsx)(k.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,a.jsx)(I.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},eV=e=>{let l,{searchParams:s,accessToken:t,showSSOBanner:n,premiumUser:o}=e,[d]=k.Z.useForm(),[c]=k.Z.useForm(),{Title:m,Paragraph:j}=es.default,[g,Z]=(0,r.useState)(""),[f,y]=(0,r.useState)(null),[b,v]=(0,r.useState)(null),[N,A]=(0,r.useState)(!1),[P,T]=(0,r.useState)(!1),[E,O]=(0,r.useState)(!1),[F,M]=(0,r.useState)(!1),[D,J]=(0,r.useState)(!1),[Y,X]=(0,r.useState)(!1),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(!1),[et,en]=(0,r.useState)(!1),[ea,er]=(0,r.useState)([]),[ei,eo]=(0,r.useState)(null),ed=(0,i.useRouter)(),[ec,em]=(0,r.useState)(null);console.log=function(){};let[eu,eh]=(0,r.useState)(""),ex="All IP Addresses Allowed";try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let ep=async()=>{try{if(!0!==o){S.ZP.error("This feature is only available for premium users. Please upgrade your account.");return}if(t){let e=await (0,u.PT)(t);er(e&&e.length>0?e:[ex])}else er([ex])}catch(e){console.error("Error fetching allowed IPs:",e),S.ZP.error("Failed to fetch allowed IPs ".concat(e)),er([ex])}finally{!0===o&&Q(!0)}},ej=async e=>{try{if(t){await (0,u.eH)(t,e.ip);let l=await (0,u.PT)(t);er(l),S.ZP.success("IP address added successfully")}}catch(e){console.error("Error adding IP:",e),S.ZP.error("Failed to add IP address ".concat(e))}finally{el(!1)}},eg=async e=>{eo(e),en(!0)},eZ=async()=>{if(ei&&t)try{await (0,u.$I)(t,ei);let e=await (0,u.PT)(t);er(e.length>0?e:[ex]),S.ZP.success("IP address deleted successfully")}catch(e){console.error("Error deleting IP:",e),S.ZP.error("Failed to delete IP address ".concat(e))}finally{en(!1),eo(null)}},ef=()=>{X(!1)},e_=["proxy_admin","proxy_admin_viewer"];(0,r.useEffect)(()=>{if(ed){let{protocol:e,host:l}=window.location;eh("".concat(e,"//").concat(l))}},[ed]),(0,r.useEffect)(()=>{(async()=>{if(null!=t){let e=[],l=await (0,u.Xd)(t,"proxy_admin_viewer");console.log("proxy admin viewer response: ",l);let s=l.users;console.log("proxy viewers response: ".concat(s)),s.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(s));let n=(await (0,u.Xd)(t,"proxy_admin")).users;n.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(n)),console.log("combinedList: ".concat(e)),y(e),em(await (0,u.lg)(t))}})()},[t]);let ey=()=>{M(!1),c.resetFields(),d.resetFields()},eb=()=>{M(!1),c.resetFields(),d.resetFields()},ev=e=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(k.Z.Item,{label:"Email",name:"user_email",className:"mb-8 mt-4",children:(0,a.jsx)(I.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},className:"mt-4",children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add member"})})]}),eS=(e,l,s)=>(0,a.jsxs)(k.Z,{form:d,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,a.jsx)(H.Z,{value:l,children:e_.map((e,l)=>(0,a.jsx)(G.Z,{value:e,children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:s,valuePropName:"user_id",className:"mt-8",children:(0,a.jsx)(I.Z,{value:s,disabled:!0})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update role"})})]}),ew=async e=>{try{if(null!=t&&null!=f){S.ZP.info("Making API Call");let l=await (0,u.pf)(t,e,null);console.log("response for team create call: ".concat(l));let s=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(l.user_id)),e.user_id===l.user_id));console.log("foundIndex: ".concat(s)),-1==s&&(console.log("updates admin with new user"),f.push(l),y(f)),S.ZP.success("Refresh tab to see updated user role"),M(!1)}}catch(e){console.error("Error creating the key:",e)}},eN=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call");let s=await (0,u.pf)(t,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(s));let n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)});let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(s.user_id)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),T(!1)}}catch(e){console.error("Error creating the key:",e)}},eI=async e=>{try{if(null!=t&&null!=f){var l;S.ZP.info("Making API Call"),e.user_email,e.user_id;let s=await (0,u.pf)(t,e,"proxy_admin"),n=(null===(l=s.data)||void 0===l?void 0:l.user_id)||s.user_id;(0,u.XO)(t,n).then(e=>{v(e),A(!0)}),console.log("response for team create call: ".concat(s));let a=f.findIndex(e=>(console.log("user.user_id=".concat(e.user_id,"; response.user_id=").concat(n)),e.user_id===s.user_id));console.log("foundIndex: ".concat(a)),-1==a&&(console.log("updates admin with new user"),f.push(s),y(f)),d.resetFields(),O(!1)}}catch(e){console.error("Error creating the key:",e)}},eA=async e=>{if(null==t)return;let l={environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}};(0,u.K_)(t,l)};return console.log("admins: ".concat(null==f?void 0:f.length)),(0,a.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,a.jsx)(m,{level:4,children:"Admin Access "}),(0,a.jsxs)(j,{children:[n&&(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc."," ",(0,a.jsx)("br",{}),(0,a.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(L.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Member Name"}),(0,a.jsx)(K.Z,{children:"Role"})]})}),(0,a.jsx)(z.Z,{children:f?f.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,a.jsxs)(B.Z,{children:[" ",(null==ec?void 0:null===(s=ec[null==e?void 0:e.user_role])||void 0===s?void 0:s.ui_label)||"-"]}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>M(!0)}),(0,a.jsx)(w.Z,{title:"Update role",visible:F,width:800,footer:null,onOk:ey,onCancel:eb,children:eS(ew,e.user_role,e.user_id)})]})]},l)}):null})]})})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)("div",{className:"flex justify-start",children:[(0,a.jsx)(p.Z,{className:"mr-4 mb-5",onClick:()=>O(!0),children:"+ Add admin"}),(0,a.jsx)(w.Z,{title:"Add admin",visible:E,width:800,footer:null,onOk:()=>{O(!1),c.resetFields(),d.resetFields()},onCancel:()=>{O(!1),A(!1),c.resetFields(),d.resetFields()},children:ev(eI)}),(0,a.jsx)(eR,{isInvitationLinkModalVisible:N,setIsInvitationLinkModalVisible:A,baseUrl:eu,invitationLinkData:b}),(0,a.jsx)(p.Z,{className:"mb-5",onClick:()=>T(!0),children:"+ Add viewer"}),(0,a.jsx)(w.Z,{title:"Add viewer",visible:P,width:800,footer:null,onOk:()=>{T(!1),c.resetFields(),d.resetFields()},onCancel:()=>{T(!1),c.resetFields(),d.resetFields()},children:ev(eN)})]})})]}),(0,a.jsxs)(x.Z,{children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(m,{level:4,children:" ✨ Security Settings"}),(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"column",gap:"1rem",marginTop:"1rem"},children:[(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:()=>!0===o?J(!0):S.ZP.error("Only premium users can add SSO"),children:"Add SSO"})}),(0,a.jsx)("div",{children:(0,a.jsx)(p.Z,{onClick:ep,children:"Allowed IPs"})})]})]}),(0,a.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,a.jsx)(w.Z,{title:"Add SSO",visible:D,width:800,footer:null,onOk:()=>{J(!1),d.resetFields()},onCancel:()=>{J(!1),d.resetFields()},children:(0,a.jsxs)(k.Z,{form:d,onFinish:e=>{eI(e),eA(e),J(!1),X(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,a.jsx)(I.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,a.jsx)(I.Z.Password,{})}),(0,a.jsx)(k.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,a.jsx)(I.Z.Password,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,a.jsxs)(w.Z,{title:"SSO Setup Instructions",visible:Y,width:800,footer:null,onOk:ef,onCancel:()=>{X(!1)},children:[(0,a.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,a.jsx)(_.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{onClick:ef,children:"Done"})})]}),(0,a.jsx)(w.Z,{title:"Manage Allowed IP Addresses",width:800,visible:$,onCancel:()=>Q(!1),footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>el(!0),children:"Add IP Address"},"add"),(0,a.jsx)(p.Z,{onClick:()=>Q(!1),children:"Close"},"close")],children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"IP Address"}),(0,a.jsx)(K.Z,{className:"text-right",children:"Action"})]})}),(0,a.jsx)(z.Z,{children:ea.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e}),(0,a.jsx)(B.Z,{className:"text-right",children:e!==ex&&(0,a.jsx)(p.Z,{onClick:()=>eg(e),color:"red",size:"xs",children:"Delete"})})]},l))})]})}),(0,a.jsx)(w.Z,{title:"Add Allowed IP Address",visible:ee,onCancel:()=>el(!1),footer:null,children:(0,a.jsxs)(k.Z,{onFinish:ej,children:[(0,a.jsx)(k.Z.Item,{name:"ip",rules:[{required:!0,message:"Please enter an IP address"}],children:(0,a.jsx)(I.Z,{placeholder:"Enter IP address"})}),(0,a.jsx)(k.Z.Item,{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add IP Address"})})]})}),(0,a.jsx)(w.Z,{title:"Confirm Delete",visible:et,onCancel:()=>en(!1),onOk:eZ,footer:[(0,a.jsx)(p.Z,{className:"mx-1",onClick:()=>eZ(),children:"Yes"},"delete"),(0,a.jsx)(p.Z,{onClick:()=>en(!1),children:"Close"},"close")],children:(0,a.jsxs)("p",{children:["Are you sure you want to delete the IP address: ",ei,"?"]})})]}),(0,a.jsxs)(ek.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access"," ",(0,a.jsxs)("a",{href:l,target:"_blank",children:[(0,a.jsx)("b",{children:l})," "]})]})]})]})},ez=s(42556),eB=s(90252),eq=e=>{let{alertingSettings:l,handleInputChange:s,handleResetField:t,handleSubmit:n,premiumUser:r}=e,[i]=k.Z.useForm();return(0,a.jsxs)(k.Z,{form:i,onFinish:()=>{console.log("INSIDE ONFINISH");let e=i.getFieldsValue(),l=Object.entries(e).every(e=>{let[l,s]=e;return"boolean"!=typeof s&&(""===s||null==s)});console.log("formData: ".concat(JSON.stringify(e),", isEmpty: ").concat(l)),l?console.log("Some form fields are empty."):n(e)},labelAlign:"left",children:[l.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{align:"center",children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),e.premium_field?r?(0,a.jsx)(k.Z.Item,{name:e.field_name,children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l)}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>s(e.field_name,l)}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}):(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})})}):(0,a.jsx)(k.Z.Item,{name:e.field_name,className:"mb-0",valuePropName:"Boolean"===e.field_type?"checked":"value",children:(0,a.jsx)(B.Z,{children:"Integer"===e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>s(e.field_name,l),className:"p-0"}):"Boolean"===e.field_type?(0,a.jsx)(ez.Z,{checked:e.field_value,onChange:l=>{s(e.field_name,l),i.setFieldsValue({[e.field_name]:l})}}):(0,a.jsx)(I.Z,{value:e.field_value,onChange:l=>s(e.field_name,l)})})}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>t(e.field_name,l),children:"Reset"})})]},l)),(0,a.jsx)("div",{children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Update Settings"})})]})},eK=e=>{let{accessToken:l,premiumUser:s}=e,[t,n]=(0,r.useState)([]);return(0,r.useEffect)(()=>{l&&(0,u.RQ)(l).then(e=>{n(e)})},[l]),(0,a.jsx)(eq,{alertingSettings:t,handleInputChange:(e,l)=>{let s=t.map(s=>s.field_name===e?{...s,field_value:l}:s);console.log("updatedSettings: ".concat(JSON.stringify(s))),n(s)},handleResetField:(e,s)=>{if(l)try{let l=t.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:l.field_default_value}:l);n(l)}catch(e){console.log("ERROR OCCURRED!")}},handleSubmit:e=>{if(!l||(console.log("formValues: ".concat(e)),null==e||void 0==e))return;let s={};t.forEach(e=>{s[e.field_name]=e.field_value});let n={...e,...s};console.log("mergedFormValues: ".concat(JSON.stringify(n)));let{slack_alerting:a,...r}=n;console.log("slack_alerting: ".concat(a,", alertingArgs: ").concat(JSON.stringify(r)));try{(0,u.jA)(l,"alerting_args",r),"boolean"==typeof a&&(!0==a?(0,u.jA)(l,"alerting",["slack"]):(0,u.jA)(l,"alerting",[])),S.ZP.success("Wait 10s for proxy to update.")}catch(e){}},premiumUser:s})},eW=s(84406);let{Title:eH,Paragraph:eG}=es.default;console.log=function(){};var eJ=e=>{let{accessToken:l,userRole:s,userID:t,premiumUser:n}=e,[i,o]=(0,r.useState)([]),[d,c]=(0,r.useState)([]),[m,h]=(0,r.useState)(!1),[g]=k.Z.useForm(),[Z,f]=(0,r.useState)(null),[y,b]=(0,r.useState)([]),[N,I]=(0,r.useState)(""),[A,P]=(0,r.useState)({}),[T,E]=(0,r.useState)([]),[O,F]=(0,r.useState)(!1),[M,D]=(0,r.useState)([]),[H,J]=(0,r.useState)(null),[Y,X]=(0,r.useState)([]),[$,Q]=(0,r.useState)(!1),[ee,el]=(0,r.useState)(null),es=e=>{T.includes(e)?E(T.filter(l=>l!==e)):E([...T,e])},et={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)",db_exceptions:"Database Exceptions (Read/Write)",daily_reports:"Weekly/Monthly Spend Reports",outage_alerts:"Outage Alerts",region_outage_alerts:"Region Outage Alerts"};(0,r.useEffect)(()=>{l&&s&&t&&(0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e),o(e.callbacks),D(e.available_callbacks);let l=e.alerts;if(console.log("alerts_data",l),l&&l.length>0){let e=l[0];console.log("_alert_info",e);let s=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",s),E(e.active_alerts),I(s),P(e.alerts_to_webhook)}c(l)})},[l,s,t]);let en=e=>T&&T.includes(e),ea=()=>{if(!l)return;let e={};d.filter(e=>"email"===e.name).forEach(l=>{var s;Object.entries(null!==(s=l.variables)&&void 0!==s?s:{}).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));n&&n.value&&(e[s]=null==n?void 0:n.value)})}),console.log("updatedVariables",e);try{(0,u.K_)(l,{general_settings:{alerting:["email"]},environment_variables:e})}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Email settings updated successfully")},em=async e=>{if(!l)return;let s={};Object.entries(e).forEach(e=>{let[l,t]=e;"callback"!==l&&(s[l]=t)});try{await (0,u.K_)(l,{environment_variables:s}),S.ZP.success("Callback added successfully"),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eu=async e=>{if(!l)return;let s=null==e?void 0:e.callback,t={};Object.entries(e).forEach(e=>{let[l,s]=e;"callback"!==l&&(t[l]=s)});try{await (0,u.K_)(l,{environment_variables:t,litellm_settings:{success_callback:[s]}}),S.ZP.success("Callback ".concat(s," added successfully")),h(!1),g.resetFields(),f(null)}catch(e){S.ZP.error("Failed to add callback: "+e,20)}},eh=e=>{console.log("inside handleSelectedCallbackChange",e),f(e.litellm_callback_name),console.log("all callbacks",M),e&&e.litellm_callback_params?(X(e.litellm_callback_params),console.log("selectedCallbackParams",Y)):X([])};return l?(console.log("callbacks: ".concat(i)),(0,a.jsxs)("div",{className:"w-full mx-4",children:[(0,a.jsx)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Logging Callbacks"}),(0,a.jsx)(er.Z,{value:"2",children:"Alerting Types"}),(0,a.jsx)(er.Z,{value:"3",children:"Alerting Settings"}),(0,a.jsx)(er.Z,{value:"4",children:"Email Alerts"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Active Logging Callbacks"}),(0,a.jsx)(x.Z,{numItems:2,children:(0,a.jsx)(L.Z,{className:"max-h-[50vh]",children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(K.Z,{children:"Callback Name"})})}),(0,a.jsx)(z.Z,{children:i.map((e,s)=>(0,a.jsxs)(W.Z,{className:"flex justify-between",children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.name})}),(0,a.jsx)(B.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"flex justify-between",children:[(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>{el(e),Q(!0)}}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,e.name),className:"ml-2",variant:"secondary",children:"Test Callback"})]})})]},s))})]})})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>F(!0),children:"Add Callback"})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(_.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from"," ",(0,a.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{}),(0,a.jsx)(K.Z,{children:"Slack Webhook URL"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(et).map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:"region_outage_alerts"==s?n?(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)}):(0,a.jsx)(p.Z,{className:"flex items-center justify-center",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Enterprise Feature"})}):(0,a.jsx)(ez.Z,{id:"switch",name:"switch",checked:en(s),onChange:()=>es(s)})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:t})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:s,type:"password",defaultValue:A&&A[s]?A[s]:N})})]},l)})})]}),(0,a.jsx)(p.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(et).forEach(l=>{let[s,t]=l,n=document.querySelector('input[name="'.concat(s,'"]'));console.log("key",s),console.log("webhookInput",n);let a=(null==n?void 0:n.value)||"";console.log("newWebhookValue",a),e[s]=a}),console.log("updatedAlertToWebhooks",e);let s={general_settings:{alert_to_webhook_url:e,alert_types:T}};console.log("payload",s);try{(0,u.K_)(l,s)}catch(e){S.ZP.error("Failed to update alerts: "+e,20)}S.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"slack"),className:"mx-2",children:"Test Alerts"})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eK,{accessToken:l,premiumUser:n})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(eH,{level:4,children:"Email Settings"}),(0,a.jsxs)(_.Z,{children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/email",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: email alerts"})," ",(0,a.jsx)("br",{})]}),(0,a.jsx)("div",{className:"flex w-full",children:d.filter(e=>"email"===e.name).map((e,l)=>{var s;return(0,a.jsx)(B.Z,{children:(0,a.jsx)("ul",{children:(0,a.jsx)(x.Z,{numItems:2,children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).map(e=>{let[l,s]=e;return(0,a.jsxs)("li",{className:"mx-2 my-2",children:[!0!=n&&("EMAIL_LOGO_URL"===l||"EMAIL_SUPPORT_CONTACT"===l)?(0,a.jsxs)("div",{children:[(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:(0,a.jsxs)(_.Z,{className:"mt-2",children:[" ","✨ ",l]})}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",disabled:!0,style:{width:"400px"}})]}):(0,a.jsxs)("div",{children:[(0,a.jsx)(_.Z,{className:"mt-2",children:l}),(0,a.jsx)(j.Z,{name:l,defaultValue:s,type:"password",style:{width:"400px"}})]}),(0,a.jsxs)("p",{style:{fontSize:"small",fontStyle:"italic"},children:["SMTP_HOST"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP host address, e.g. `smtp.resend.com`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PORT"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP port number, e.g. `587`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_USERNAME"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the SMTP username, e.g. `username`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"SMTP_PASSWORD"===l&&(0,a.jsx)("span",{style:{color:"red"},children:" Required * "}),"SMTP_SENDER_EMAIL"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Enter the sender email address, e.g. `sender@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"TEST_EMAIL_ADDRESS"===l&&(0,a.jsxs)("div",{style:{color:"gray"},children:["Email Address to send `Test Email Alert` to. example: `info@berri.ai`",(0,a.jsx)("span",{style:{color:"red"},children:" Required * "})]}),"EMAIL_LOGO_URL"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the Logo that appears in the email, pass a url to your logo"}),"EMAIL_SUPPORT_CONTACT"===l&&(0,a.jsx)("div",{style:{color:"gray"},children:"(Optional) Customize the support email address that appears in the email. Default is support@berri.ai"})]})]},l)})})})},l)})}),(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>ea(),children:"Save Changes"}),(0,a.jsx)(p.Z,{onClick:()=>(0,u.jE)(l,"email"),className:"mx-2",children:"Test Email Alerts"})]})})]})]})}),(0,a.jsxs)(w.Z,{title:"Add Logging Callback",visible:O,width:800,onCancel:()=>F(!1),footer:null,children:[(0,a.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/logging",className:"mb-8 mt-4",target:"_blank",style:{color:"blue"},children:" LiteLLM Docs: Logging"}),(0,a.jsx)(k.Z,{form:g,onFinish:eu,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(eW.Z,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,a.jsx)(v.default,{onChange:e=>{let l=M[e];l&&(console.log(l.ui_callback_name),eh(l))},children:M&&Object.values(M).map(e=>(0,a.jsx)(G.Z,{value:e.litellm_callback_name,children:e.ui_callback_name},e.litellm_callback_name))})}),Y&&Y.map(e=>(0,a.jsx)(eW.Z,{label:e,name:e,rules:[{required:!0,message:"Please enter the value for "+e}],children:(0,a.jsx)(j.Z,{type:"password"})},e)),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]}),(0,a.jsx)(w.Z,{visible:$,width:800,title:"Edit ".concat(null==ee?void 0:ee.name," Settings"),onCancel:()=>Q(!1),footer:null,children:(0,a.jsxs)(k.Z,{form:g,onFinish:em,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsx)(a.Fragment,{children:ee&&ee.variables&&Object.entries(ee.variables).map(e=>{let[l,s]=e;return(0,a.jsx)(eW.Z,{label:l,name:l,children:(0,a.jsx)(j.Z,{type:"password",defaultValue:s})},l)})}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:eY}=v.default;var eX=e=>{let{models:l,accessToken:s,routerSettings:t,setRouterSettings:n}=e,[i]=k.Z.useForm(),[o,d]=(0,r.useState)(!1),[c,m]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>d(!0),children:"+ Add Fallbacks"}),(0,a.jsx)(w.Z,{title:"Add Fallbacks",visible:o,width:800,footer:null,onOk:()=>{d(!1),i.resetFields()},onCancel:()=>{d(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:e=>{console.log(e);let{model_name:l,models:a}=e,r=[...t.fallbacks||[],{[l]:a}],o={...t,fallbacks:r};console.log(o);try{(0,u.K_)(s,{router_settings:o}),n(o)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully"),d(!1),i.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,a.jsx)(H.Z,{defaultValue:c,children:l&&l.map((e,l)=>(0,a.jsx)(G.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,a.jsx)(k.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,a.jsx)(em.Z,{value:l,children:l&&l.filter(e=>e!=c).map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},e$=s(12968);async function eQ(e,l){console.log=function(){},console.log("isLocal:",!1);let s=window.location.origin,t=new e$.ZP.OpenAI({apiKey:l,baseURL:s,dangerouslyAllowBrowser:!0});try{let l=await t.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});S.ZP.success((0,a.jsxs)("span",{children:["Test model=",(0,a.jsx)("strong",{children:e}),", received model=",(0,a.jsx)("strong",{children:l.model}),". See"," ",(0,a.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}let e0={ttl:3600,lowest_latency_buffer:0},e1=e=>{let{selectedStrategy:l,strategyArgs:s,paramExplanation:t}=e;return(0,a.jsxs)(g.Z,{children:[(0,a.jsx)(f.Z,{className:"text-sm font-medium text-tremor-content-strong dark:text-dark-tremor-content-strong",children:"Routing Strategy Specific Args"}),(0,a.jsx)(Z.Z,{children:"latency-based-routing"==l?(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(s).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:t[l]})]}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]})}):(0,a.jsx)(_.Z,{children:"No specific settings"})})]})};var e2=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)({}),[d,c]=(0,r.useState)({}),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(!1),[b]=k.Z.useForm(),[v,w]=(0,r.useState)(null),[N,I]=(0,r.useState)(null),[C,P]=(0,r.useState)(null),T={routing_strategy_args:"(dict) Arguments to pass to the routing strategy",routing_strategy:"(string) Routing strategy to use",allowed_fails:"(int) Number of times a deployment can fail before being added to cooldown",cooldown_time:"(int) time in seconds to cooldown a deployment after failure",num_retries:"(int) Number of retries for failed requests. Defaults to 0.",timeout:"(float) Timeout for requests. Defaults to None.",retry_after:"(int) Minimum time to wait before retrying a failed request",ttl:"(int) Sliding window to look back over when calculating the average latency of a deployment. Default - 1 hour (in seconds).",lowest_latency_buffer:"(float) Shuffle between deployments within this % of the lowest latency. Default - 0 (i.e. always pick lowest latency)."};(0,r.useEffect)(()=>{l&&s&&t&&((0,u.BL)(l,t,s).then(e=>{console.log("callbacks",e);let l=e.router_settings;"model_group_retry_policy"in l&&delete l.model_group_retry_policy,o(l)}),(0,u.YU)(l).then(e=>{g(e)}))},[l,s,t]);let E=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(i.fallbacks)),i.fallbacks.map(l=>(e in l&&delete l[e],l));try{await (0,u.K_)(l,{router_settings:i}),o({...i}),I(i.routing_strategy),S.ZP.success("Router settings updated successfully")}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}}},O=(e,l)=>{g(m.map(s=>s.field_name===e?{...s,field_value:l}:s))},R=(e,s)=>{if(!l)return;let t=m[s].field_value;if(null!=t&&void 0!=t)try{(0,u.jA)(l,e,t);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:!0}:l);g(s)}catch(e){}},F=(e,s)=>{if(l)try{(0,u.ao)(l,e);let s=m.map(l=>l.field_name===e?{...l,stored_in_db:null,field_value:null}:l);g(s)}catch(e){}},J=e=>{if(!l)return;console.log("router_settings",e);let s=Object.fromEntries(Object.entries(e).map(e=>{let[l,s]=e;if("routing_strategy_args"!==l&&"routing_strategy"!==l){var t;return[l,(null===(t=document.querySelector('input[name="'.concat(l,'"]')))||void 0===t?void 0:t.value)||s]}if("routing_strategy"==l)return[l,N];if("routing_strategy_args"==l&&"latency-based-routing"==N){let e={},l=document.querySelector('input[name="lowest_latency_buffer"]'),s=document.querySelector('input[name="ttl"]');return(null==l?void 0:l.value)&&(e.lowest_latency_buffer=Number(l.value)),(null==s?void 0:s.value)&&(e.ttl=Number(s.value)),console.log("setRoutingStrategyArgs: ".concat(e)),["routing_strategy_args",e]}return null}).filter(e=>null!=e));console.log("updatedVariables",s);try{(0,u.K_)(l,{router_settings:s})}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("router settings updated successfully")};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsxs)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,a.jsxs)(eo.Z,{variant:"line",defaultValue:"1",children:[(0,a.jsx)(er.Z,{value:"1",children:"Loadbalancing"}),(0,a.jsx)(er.Z,{value:"2",children:"Fallbacks"}),(0,a.jsx)(er.Z,{value:"3",children:"General"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,a.jsx)(y.Z,{children:"Router Settings"}),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"})]})}),(0,a.jsx)(z.Z,{children:Object.entries(i).filter(e=>{let[l,s]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l&&"routing_strategy_args"!=l}).map(e=>{let[l,s]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:l}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:T[l]})]}),(0,a.jsx)(B.Z,{children:"routing_strategy"==l?(0,a.jsxs)(H.Z,{defaultValue:s,className:"w-full max-w-md",onValueChange:I,children:[(0,a.jsx)(G.Z,{value:"usage-based-routing",children:"usage-based-routing"}),(0,a.jsx)(G.Z,{value:"latency-based-routing",children:"latency-based-routing"}),(0,a.jsx)(G.Z,{value:"simple-shuffle",children:"simple-shuffle"})]}):(0,a.jsx)(j.Z,{name:l,defaultValue:"object"==typeof s?JSON.stringify(s,null,2):s.toString()})})]},l)})})]}),(0,a.jsx)(e1,{selectedStrategy:N,strategyArgs:i&&i.routing_strategy_args&&Object.keys(i.routing_strategy_args).length>0?i.routing_strategy_args:e0,paramExplanation:T})]}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(p.Z,{className:"mt-2",onClick:()=>J(i),children:"Save Changes"})})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Model Name"}),(0,a.jsx)(K.Z,{children:"Fallbacks"})]})}),(0,a.jsx)(z.Z,{children:i.fallbacks&&i.fallbacks.map((e,s)=>Object.entries(e).map(e=>{let[t,n]=e;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:t}),(0,a.jsx)(B.Z,{children:Array.isArray(n)?n.join(", "):n}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(p.Z,{onClick:()=>eQ(t,l),children:"Test Fallback"})}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>E(t)})})]},s.toString()+t)}))})]}),(0,a.jsx)(eX,{models:(null==n?void 0:n.data)?n.data.map(e=>e.model_name):[],accessToken:l,routerSettings:i,setRouterSettings:o})]}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Setting"}),(0,a.jsx)(K.Z,{children:"Value"}),(0,a.jsx)(K.Z,{children:"Status"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:m.filter(e=>"TypedDictionary"!==e.field_type).map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(_.Z,{children:e.field_name}),(0,a.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:e.field_description})]}),(0,a.jsx)(B.Z,{children:"Integer"==e.field_type?(0,a.jsx)(A.Z,{step:1,value:e.field_value,onChange:l=>O(e.field_name,l)}):null}),(0,a.jsx)(B.Z,{children:!0==e.stored_in_db?(0,a.jsx)(D.Z,{icon:eB.Z,className:"text-white",children:"In DB"}):!1==e.stored_in_db?(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"In Config"}):(0,a.jsx)(D.Z,{className:"text-gray bg-white outline",children:"Not Set"})}),(0,a.jsxs)(B.Z,{children:[(0,a.jsx)(p.Z,{onClick:()=>R(e.field_name,l),children:"Update"}),(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>F(e.field_name,l),children:"Reset"})]})]},l))})]})})})]})]})}):null},e4=s(98786),e5=s(74325),e8=e=>{let{value:l={},onChange:s}=e,[t,n]=(0,r.useState)(Object.entries(l)),i=e=>{let l=t.filter((l,s)=>s!==e);n(l),null==s||s(Object.fromEntries(l))},o=(e,l,a)=>{let r=[...t];r[e]=[l,a],n(r),null==s||s(Object.fromEntries(r))};return(0,a.jsxs)("div",{children:[t.map((e,l)=>{let[s,t]=e;return(0,a.jsxs)(c.Z,{style:{display:"flex",marginBottom:8},align:"start",children:[(0,a.jsx)(j.Z,{placeholder:"Header Name",value:s,onChange:e=>o(l,e.target.value,t)}),(0,a.jsx)(j.Z,{placeholder:"Header Value",value:t,onChange:e=>o(l,s,e.target.value)}),(0,a.jsx)(e4.Z,{onClick:()=>i(l)})]},l)}),(0,a.jsx)(C.ZP,{type:"dashed",onClick:()=>{n([...t,["",""]])},icon:(0,a.jsx)(e5.Z,{}),children:"Add Header"})]})};let{Option:e3}=v.default;var e6=e=>{let{accessToken:l,setPassThroughItems:s,passThroughItems:t}=e,[n]=k.Z.useForm(),[i,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)("");return(0,a.jsxs)("div",{children:[(0,a.jsx)(p.Z,{className:"mx-auto",onClick:()=>o(!0),children:"+ Add Pass-Through Endpoint"}),(0,a.jsx)(w.Z,{title:"Add Pass-Through Endpoint",visible:i,width:800,footer:null,onOk:()=>{o(!1),n.resetFields()},onCancel:()=>{o(!1),n.resetFields()},children:(0,a.jsxs)(k.Z,{form:n,onFinish:e=>{console.log(e);let a=[...t,{headers:e.headers,path:e.path,target:e.target}];try{(0,u.Vt)(l,e),s(a)}catch(e){S.ZP.error("Failed to update router settings: "+e,20)}S.ZP.success("Pass through endpoint successfully added"),o(!1),n.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Path",name:"path",rules:[{required:!0,message:"The route to be added to the LiteLLM Proxy Server."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Target",name:"target",rules:[{required:!0,message:"The URL to which requests for this path should be forwarded."}],help:"required",children:(0,a.jsx)(j.Z,{})}),(0,a.jsx)(k.Z.Item,{label:"Headers",name:"headers",rules:[{required:!0,message:"Key-value pairs of headers to be forwarded with the request. You can set any key value pair here and it will be forwarded to your target endpoint"}],help:"required",children:(0,a.jsx)(e8,{})})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Add Pass-Through Endpoint"})})]})})]})},e7=e=>{let{accessToken:l,userRole:s,userID:t,modelData:n}=e,[i,o]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&(0,u.mp)(l).then(e=>{o(e.endpoints)})},[l,s,t]);let d=(e,s)=>{if(l)try{(0,u.EG)(l,e);let s=i.filter(l=>l.path!==e);o(s),S.ZP.success("Endpoint deleted successfully.")}catch(e){}};return l?(0,a.jsx)("div",{className:"w-full mx-4",children:(0,a.jsx)(ei.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Path"}),(0,a.jsx)(K.Z,{children:"Target"}),(0,a.jsx)(K.Z,{children:"Headers"}),(0,a.jsx)(K.Z,{children:"Action"})]})}),(0,a.jsx)(z.Z,{children:i.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:(0,a.jsx)(_.Z,{children:e.path})}),(0,a.jsx)(B.Z,{children:e.target}),(0,a.jsx)(B.Z,{children:JSON.stringify(e.headers)}),(0,a.jsx)(B.Z,{children:(0,a.jsx)(U.Z,{icon:M.Z,color:"red",onClick:()=>d(e.path,l),children:"Reset"})})]},l))})]}),(0,a.jsx)(e6,{accessToken:l,setPassThroughItems:o,passThroughItems:i})]})})}):null},e9=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n}=e,[r]=k.Z.useForm(),i=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),r.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Create Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),r.resetFields()},onCancel:()=>{t(!1),r.resetFields()},children:(0,a.jsxs)(k.Z,{form:r,onFinish:i,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Create Budget"})})]})})},le=e=>{let{isModalVisible:l,accessToken:s,setIsModalVisible:t,setBudgetList:n,existingBudget:r}=e,[i]=k.Z.useForm(),o=async e=>{if(null!=s&&void 0!=s)try{S.ZP.info("Making API Call");let l=await (0,u.Zr)(s,e);console.log("key create Response:",l),n(e=>e?[...e,l]:[l]),S.ZP.success("API Key Created"),i.resetFields()}catch(e){console.error("Error creating the key:",e),S.ZP.error("Error creating the key: ".concat(e),20)}};return(0,a.jsx)(w.Z,{title:"Edit Budget",visible:l,width:800,footer:null,onOk:()=>{t(!1),i.resetFields()},onCancel:()=>{t(!1),i.resetFields()},children:(0,a.jsxs)(k.Z,{form:i,onFinish:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",initialValues:r,children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(k.Z.Item,{label:"Budget ID",name:"budget_id",rules:[{required:!0,message:"Please input a human-friendly name for the budget"}],help:"A human-friendly name for the budget",children:(0,a.jsx)(j.Z,{placeholder:""})}),(0,a.jsx)(k.Z.Item,{label:"Max Tokens per minute",name:"tpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{label:"Max Requests per minute",name:"rpm_limit",help:"Default is model limit.",children:(0,a.jsx)(A.Z,{step:1,precision:2,width:200})}),(0,a.jsxs)(g.Z,{className:"mt-20 mb-8",children:[(0,a.jsx)(f.Z,{children:(0,a.jsx)("b",{children:"Optional Settings"})}),(0,a.jsxs)(Z.Z,{children:[(0,a.jsx)(k.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,a.jsx)(A.Z,{step:.01,precision:2,width:200})}),(0,a.jsx)(k.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",children:(0,a.jsxs)(v.default,{defaultValue:null,placeholder:"n/a",children:[(0,a.jsx)(v.default.Option,{value:"24h",children:"daily"}),(0,a.jsx)(v.default.Option,{value:"7d",children:"weekly"}),(0,a.jsx)(v.default.Option,{value:"30d",children:"monthly"})]})})]})]})]}),(0,a.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,a.jsx)(C.ZP,{htmlType:"submit",children:"Edit Budget"})})]})})},ll=e=>{let{accessToken:l}=e,[s,t]=(0,r.useState)(!1),[n,i]=(0,r.useState)(!1),[o,d]=(0,r.useState)(null),[c,m]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&(0,u.O3)(l).then(e=>{m(e)})},[l]);let h=async(e,s)=>{null!=l&&(d(c[s]),i(!0))},x=async(e,s)=>{if(null==l)return;S.ZP.info("Request made"),await (0,u.NV)(l,e);let t=[...c];t.splice(s,1),m(t),S.ZP.success("Budget Deleted.")};return(0,a.jsxs)("div",{className:"w-full mx-auto flex-auto overflow-y-auto m-8 p-2",children:[(0,a.jsx)(p.Z,{size:"sm",variant:"primary",className:"mb-2",onClick:()=>t(!0),children:"+ Create Budget"}),(0,a.jsx)(e9,{accessToken:l,isModalVisible:s,setIsModalVisible:t,setBudgetList:m}),o&&(0,a.jsx)(le,{accessToken:l,isModalVisible:n,setIsModalVisible:i,setBudgetList:m,existingBudget:o}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(_.Z,{children:"Create a budget to assign to customers."}),(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Budget ID"}),(0,a.jsx)(K.Z,{children:"Max Budget"}),(0,a.jsx)(K.Z,{children:"TPM"}),(0,a.jsx)(K.Z,{children:"RPM"})]})}),(0,a.jsx)(z.Z,{children:c.map((e,l)=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.budget_id}),(0,a.jsx)(B.Z,{children:e.max_budget?e.max_budget:"n/a"}),(0,a.jsx)(B.Z,{children:e.tpm_limit?e.tpm_limit:"n/a"}),(0,a.jsx)(B.Z,{children:e.rpm_limit?e.rpm_limit:"n/a"}),(0,a.jsx)(U.Z,{icon:R.Z,size:"sm",onClick:()=>h(e.budget_id,l)}),(0,a.jsx)(U.Z,{icon:M.Z,size:"sm",onClick:()=>x(e.budget_id,l)})]},l))})]})]}),(0,a.jsxs)("div",{className:"mt-5",children:[(0,a.jsx)(_.Z,{className:"text-base",children:"How to use budget id"}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"Assign Budget to Customer"}),(0,a.jsx)(er.Z,{children:"Test it (Curl)"}),(0,a.jsx)(er.Z,{children:"Test it (OpenAI SDK)"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:"\ncurl -X POST --location '/end_user/new' \n-H 'Authorization: Bearer ' \n-H 'Content-Type: application/json' \n-d '{\"user_id\": \"my-customer-id', \"budget_id\": \"\"}' # \uD83D\uDC48 KEY CHANGE\n\n "})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"bash",children:'\ncurl -X POST --location \'/chat/completions\' \n-H \'Authorization: Bearer \' \n-H \'Content-Type: application/json\' \n-d \'{\n "model": "gpt-3.5-turbo\', \n "messages":[{"role": "user", "content": "Hey, how\'s it going?"}],\n "user": "my-customer-id"\n}\' # \uD83D\uDC48 KEY CHANGE\n\n '})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'from openai import OpenAI\nclient = OpenAI(\n base_url="",\n api_key=""\n)\n\ncompletion = client.chat.completions.create(\n model="gpt-3.5-turbo",\n messages=[\n {"role": "system", "content": "You are a helpful assistant."},\n {"role": "user", "content": "Hello!"}\n ],\n user="my-customer-id"\n)\n\nprint(completion.choices[0].message)'})})]})]})]})]})},ls=s(41134),lt=e=>{let{proxySettings:l}=e,s="";return l&&l.PROXY_BASE_URL&&void 0!==l.PROXY_BASE_URL&&(s=l.PROXY_BASE_URL),(0,a.jsx)(a.Fragment,{children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsxs)("div",{className:"mb-5",children:[(0,a.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,a.jsx)(_.Z,{className:"mt-2 mb-2",children:"LiteLLM is OpenAI Compatible. This means your API Key works with the OpenAI SDK. Just replace the base_url to point to your litellm proxy. Example Below "}),(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{children:[(0,a.jsx)(er.Z,{children:"OpenAI Python SDK"}),(0,a.jsx)(er.Z,{children:"LlamaIndex"}),(0,a.jsx)(er.Z,{children:"Langchain Py"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="'.concat(s,'" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="gpt-3.5-turbo", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="azure-gpt-3.5", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="'.concat(s,'", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="').concat(s,'",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,a.jsx)(ed.Z,{children:(0,a.jsx)(eI.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="'.concat(s,'",\n model = "gpt-3.5-turbo",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})})};async function ln(e,l,s,t){console.log=function(){},console.log("isLocal:",!1);let n=window.location.origin,a=new e$.ZP.OpenAI({apiKey:t,baseURL:n,dangerouslyAllowBrowser:!0});try{for await(let t of(await a.chat.completions.create({model:s,stream:!0,messages:[{role:"user",content:e}]})))console.log(t),t.choices[0].delta.content&&l(t.choices[0].delta.content)}catch(e){S.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var la=e=>{let{accessToken:l,token:s,userRole:t,userID:n}=e,[i,o]=(0,r.useState)(""),[d,c]=(0,r.useState)(""),[m,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)(void 0),[y,b]=(0,r.useState)([]);(0,r.useEffect)(()=>{l&&s&&t&&n&&(async()=>{try{let e=await (0,u.So)(l,n,t);if(console.log("model_info:",e),(null==e?void 0:e.data.length)>0){let l=e.data.map(e=>({value:e.id,label:e.id}));if(console.log(l),l.length>0){let e=Array.from(new Set(l));console.log("Unique models:",e),e.sort((e,l)=>e.label.localeCompare(l.label)),console.log("Model info:",y),b(e)}f(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,n,t]);let k=(e,l)=>{g(s=>{let t=s[s.length-1];return t&&t.role===e?[...s.slice(0,s.length-1),{role:e,content:t.content+l}]:[...s,{role:e,content:l}]})},S=async()=>{if(""!==d.trim()&&i&&s&&t&&n){g(e=>[...e,{role:"user",content:d}]);try{Z&&await ln(d,e=>k("assistant",e),Z,i)}catch(e){console.error("Error fetching model response",e),k("assistant","Error fetching model response")}c("")}};if(t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=es.default;return(0,a.jsxs)("div",{children:[(0,a.jsx)(e,{level:1,children:"Access Denied"}),(0,a.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,a.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,a.jsx)(x.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,a.jsx)(L.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsx)(eo.Z,{children:(0,a.jsx)(er.Z,{children:"Chat"})}),(0,a.jsx)(ec.Z,{children:(0,a.jsxs)(ed.Z,{children:[(0,a.jsx)("div",{className:"sm:max-w-2xl",children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"API Key"}),(0,a.jsx)(j.Z,{placeholder:"Type API Key here",type:"password",onValueChange:o,value:i})]}),(0,a.jsxs)(h.Z,{className:"mx-2",children:[(0,a.jsx)(_.Z,{children:"Select Model:"}),(0,a.jsx)(v.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),f(e)},options:y,style:{width:"200px"}})]})]})}),(0,a.jsxs)(V.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,a.jsx)(q.Z,{children:(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{})})}),(0,a.jsx)(z.Z,{children:m.map((e,l)=>(0,a.jsx)(W.Z,{children:(0,a.jsx)(B.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,a.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,a.jsxs)("div",{className:"flex",children:[(0,a.jsx)(j.Z,{type:"text",value:d,onChange:e=>c(e.target.value),onKeyDown:e=>{"Enter"===e.key&&S()},placeholder:"Type your message..."}),(0,a.jsx)(p.Z,{onClick:S,className:"ml-2",children:"Send"})]})})]})})]})})})})},lr=s(33509),li=s(95781);let{Sider:lo}=lr.default,ld=["Admin","Admin Viewer","Internal User","Internal Viewer"];var lc=e=>{let{setPage:l,userRole:s,defaultSelectedKey:t}=e;return"Admin Viewer"==s?(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,a.jsx)(lo,{width:120,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["4"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9")]})})}):(0,a.jsx)(lr.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,a.jsx)(lo,{width:145,children:(0,a.jsxs)(li.Z,{mode:"inline",defaultSelectedKeys:t||["1"],style:{height:"100%",borderRight:0},children:[(0,a.jsx)(li.Z.Item,{onClick:()=>l("api-keys"),children:(0,a.jsx)(_.Z,{children:"Virtual Keys"})},"1"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("llm-playground"),children:(0,a.jsx)(_.Z,{children:"Test Key"})},"3"),"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("models"),children:(0,a.jsx)(_.Z,{children:"Models"})},"2"):null,ld.includes(s)?(0,a.jsx)(li.Z.Item,{onClick:()=>l("usage"),children:(0,a.jsx)(_.Z,{children:"Usage"})},"4"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("teams"),children:(0,a.jsx)(_.Z,{children:"Teams"})},"6"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("users"),children:(0,a.jsx)(_.Z,{children:"Internal Users"})},"5"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("settings"),children:(0,a.jsx)(_.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("caching"),children:(0,a.jsx)(_.Z,{children:"Caching"})},"9"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("budgets"),children:(0,a.jsx)(_.Z,{children:"Budgets"})},"10"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("general-settings"),children:(0,a.jsx)(_.Z,{children:"Router Settings"})},"11"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("pass-through-settings"),children:(0,a.jsx)(_.Z,{children:"Pass-Through"})},"12"):null,"Admin"==s?(0,a.jsx)(li.Z.Item,{onClick:()=>l("admin-panel"),children:(0,a.jsx)(_.Z,{children:"Admin Settings"})},"13"):null,(0,a.jsx)(li.Z.Item,{onClick:()=>l("api_ref"),children:(0,a.jsx)(_.Z,{children:"API Reference"})},"14"),(0,a.jsx)(li.Z.Item,{onClick:()=>l("model-hub"),children:(0,a.jsx)(_.Z,{children:"Model Hub"})},"16")]})})})},lm=s(67989),lu=s(52703);console.log("process.env.NODE_ENV","production"),console.log=function(){};let lh=e=>null!==e&&("Admin"===e||"Admin Viewer"===e);var lx=e=>{let{accessToken:l,token:s,userRole:t,userID:n,keys:i,premiumUser:o}=e,d=new Date,[c,m]=(0,r.useState)([]),[j,g]=(0,r.useState)([]),[Z,f]=(0,r.useState)([]),[b,v]=(0,r.useState)([]),[k,S]=(0,r.useState)([]),[w,N]=(0,r.useState)([]),[I,A]=(0,r.useState)([]),[C,P]=(0,r.useState)([]),[T,E]=(0,r.useState)([]),[O,R]=(0,r.useState)([]),[F,M]=(0,r.useState)({}),[D,U]=(0,r.useState)([]),[J,Y]=(0,r.useState)(""),[$,Q]=(0,r.useState)(["all-tags"]),[ee,el]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date}),es=new Date(d.getFullYear(),d.getMonth(),1),et=new Date(d.getFullYear(),d.getMonth()+1,0),ep=e_(es),ej=e_(et);function eg(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}console.log("keys in usage",i),console.log("premium user in usage",o),(0,r.useEffect)(()=>{ef(ee.from,ee.to)},[ee,$]);let eZ=async(e,s,t)=>{if(!e||!s||!l)return;s.setHours(23,59,59,999),e.setHours(0,0,0,0),console.log("uiSelectedKey",t);let n=await (0,u.b1)(l,t,e.toISOString(),s.toISOString());console.log("End user data updated successfully",n),v(n)},ef=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),N((await (0,u.J$)(l,e.toISOString(),s.toISOString(),0===$.length?void 0:$)).spend_per_tag),console.log("Tag spend data updated successfully"))};function e_(e){let l=e.getFullYear(),s=e.getMonth()+1,t=e.getDate();return"".concat(l,"-").concat(s<10?"0"+s:s,"-").concat(t<10?"0"+t:t)}console.log("Start date is ".concat(ep)),console.log("End date is ".concat(ej));let ey=async(e,l,s)=>{try{let s=await e();l(s)}catch(e){console.error(s,e)}},eb=()=>ey(()=>l?(0,u.FC)(l):Promise.reject("No access token"),m,"Error fetching overall spend"),ev=()=>ey(()=>l&&s?(0,u.OU)(l,s,ep,ej):Promise.reject("No access token or token"),R,"Error fetching provider spend"),ek=async()=>{l&&await ey(async()=>(await (0,u.tN)(l)).map(e=>({key:(e.key_alias||e.key_name||e.api_key).substring(0,10),spend:e.total_spend})),g,"Error fetching top keys")},eS=async()=>{l&&await ey(async()=>(await (0,u.Au)(l)).map(e=>({key:e.model,spend:e.total_spend})),f,"Error fetching top models")},ew=async()=>{l&&await ey(async()=>{let e=await (0,u.mR)(l);return S(e.daily_spend),P(e.teams),e.total_spend_per_team.map(e=>({name:e.team_id||"",value:(e.total_spend||0).toFixed(2)}))},E,"Error fetching team spend")},eN=()=>{l&&ey(async()=>(await (0,u.X)(l)).tag_names,A,"Error fetching tag names")},eI=()=>{l&&ey(()=>{var e,s;return(0,u.J$)(l,null===(e=ee.from)||void 0===e?void 0:e.toISOString(),null===(s=ee.to)||void 0===s?void 0:s.toISOString(),void 0)},e=>N(e.spend_per_tag),"Error fetching top tags")},eA=()=>{l&&ey(()=>(0,u.b1)(l,null,void 0,void 0),v,"Error fetching top end users")},eC=()=>{l&&ey(()=>(0,u.wd)(l,ep,ej),M,"Error fetching global activity")},eP=()=>{l&&ey(()=>(0,u.xA)(l,ep,ej),U,"Error fetching global activity per model")};return(0,r.useEffect)(()=>{l&&s&&t&&n&&(eb(),ev(),ek(),eS(),eC(),eP(),lh(t)&&(ew(),eN(),eI(),eA()))},[l,s,t,n,ep,ej]),(0,a.jsx)("div",{style:{width:"100%"},className:"p-8",children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{className:"mt-2",children:[(0,a.jsx)(er.Z,{children:"All Up"}),lh(t)?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(er.Z,{children:"Team Based Usage"}),(0,a.jsx)(er.Z,{children:"Customer Usage"}),(0,a.jsx)(er.Z,{children:"Tag Based Usage"})]}):(0,a.jsx)(a.Fragment,{children:(0,a.jsx)("div",{})})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(ei.Z,{children:[(0,a.jsxs)(eo.Z,{variant:"solid",className:"mt-1",children:[(0,a.jsx)(er.Z,{children:"Cost"}),(0,a.jsx)(er.Z,{children:"Activity"})]}),(0,a.jsxs)(ec.Z,{children:[(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[100vh] w-full",children:[(0,a.jsx)(X,{userID:n,userRole:t,accessToken:l,userSpend:null,selectedTeam:null,userMaxBudget:null}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Monthly Spend"}),(0,a.jsx)(ex.Z,{data:c,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top API Keys"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:j,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Top Models"}),(0,a.jsx)(ex.Z,{className:"mt-4 h-40",data:Z,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,a.jsx)(h.Z,{numColSpan:1}),(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"✨ Spend by Provider"}),o?(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(lu.Z,{className:"mt-4 h-40",variant:"pie",data:O,index:"provider",category:"spend"})}),(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsxs)(V.Z,{children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Provider"}),(0,a.jsx)(K.Z,{children:"Spend"})]})}),(0,a.jsx)(z.Z,{children:O.map(e=>(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.provider}),(0,a.jsx)(B.Z,{children:1e-5>parseFloat(e.spend.toFixed(2))?"less than 0.00":e.spend.toFixed(2)})]},e.provider))})]})})]})}):(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to use this feature"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})]})]})})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:1,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"All Up"}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(F.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["api_requests"],onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(F.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:F.daily_data,valueFormatter:eg,index:"date",colors:["cyan"],categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]}),o?(0,a.jsx)(a.Fragment,{children:D.map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["total_tokens"],valueFormatter:eg,onValueChange:e=>console.log(e)})]})]})]},l))}):(0,a.jsx)(a.Fragment,{children:D&&D.length>0&&D.slice(0,1).map((e,l)=>(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"✨ Activity by Model"}),(0,a.jsx)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:"Upgrade to see analytics for all models"}),(0,a.jsx)(p.Z,{variant:"primary",className:"mb-2",children:(0,a.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:e.model}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["API Requests ",eg(e.sum_api_requests)]}),(0,a.jsx)(eh.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],categories:["api_requests"],valueFormatter:eg,onValueChange:e=>console.log(e)})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsxs)(en.Z,{style:{fontSize:"15px",fontWeight:"normal",color:"#535452"},children:["Tokens ",eg(e.sum_total_tokens)]}),(0,a.jsx)(ex.Z,{className:"h-40",data:e.daily_data,index:"date",colors:["cyan"],valueFormatter:eg,categories:["total_tokens"],onValueChange:e=>console.log(e)})]})]})]})]},l))})]})})]})]})}),(0,a.jsx)(ed.Z,{children:(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,a.jsxs)(h.Z,{numColSpan:2,children:[(0,a.jsxs)(L.Z,{className:"mb-2",children:[(0,a.jsx)(y.Z,{children:"Total Spend Per Team"}),(0,a.jsx)(lm.Z,{data:T})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Daily Spend Per Team"}),(0,a.jsx)(ex.Z,{className:"h-72",data:k,showLegend:!0,index:"date",categories:C,yAxisWidth:80,stack:!0})]})]}),(0,a.jsx)(h.Z,{numColSpan:2})]})}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)("p",{className:"mb-2 text-gray-500 italic text-[12px]",children:["Customers of your LLM API calls. Tracked when a `user` param is passed in your LLM calls ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/users",target:"_blank",children:"docs here"})]}),(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Time Range"}),(0,a.jsx)(ea.Z,{enableSelect:!0,value:ee,onValueChange:e=>{el(e),eZ(e.from,e.to,null)}})]}),(0,a.jsxs)(h.Z,{children:[(0,a.jsx)(_.Z,{children:"Select Key"}),(0,a.jsxs)(H.Z,{defaultValue:"all-keys",children:[(0,a.jsx)(G.Z,{value:"all-keys",onClick:()=>{eZ(ee.from,ee.to,null)},children:"All Keys"},"all-keys"),null==i?void 0:i.map((e,l)=>e&&null!==e.key_alias&&e.key_alias.length>0?(0,a.jsx)(G.Z,{value:String(l),onClick:()=>{eZ(ee.from,ee.to,e.token)},children:e.key_alias},l):null)]})]})]}),(0,a.jsx)(L.Z,{className:"mt-4",children:(0,a.jsxs)(V.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,a.jsx)(q.Z,{children:(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(K.Z,{children:"Customer"}),(0,a.jsx)(K.Z,{children:"Spend"}),(0,a.jsx)(K.Z,{children:"Total Events"})]})}),(0,a.jsx)(z.Z,{children:null==b?void 0:b.map((e,l)=>{var s;return(0,a.jsxs)(W.Z,{children:[(0,a.jsx)(B.Z,{children:e.end_user}),(0,a.jsx)(B.Z,{children:null===(s=e.total_spend)||void 0===s?void 0:s.toFixed(4)}),(0,a.jsx)(B.Z,{children:e.total_count})]},l)})})]})})]}),(0,a.jsxs)(ed.Z,{children:[(0,a.jsxs)(x.Z,{numItems:2,children:[(0,a.jsx)(h.Z,{numColSpan:1,children:(0,a.jsx)(ea.Z,{className:"mb-4",enableSelect:!0,value:ee,onValueChange:e=>{el(e),ef(e.from,e.to)}})}),(0,a.jsx)(h.Z,{children:o?(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsx)(eu.Z,{value:String(e),children:e},e))]})}):(0,a.jsx)("div",{children:(0,a.jsxs)(em.Z,{value:$,onValueChange:e=>Q(e),children:[(0,a.jsx)(eu.Z,{value:"all-tags",onClick:()=>Q(["all-tags"]),children:"All Tags"},"all-tags"),I&&I.filter(e=>"all-tags"!==e).map((e,l)=>(0,a.jsxs)(G.Z,{value:String(e),disabled:!0,children:["✨ ",e," (Enterprise only Feature)"]},e))]})})})]}),(0,a.jsxs)(x.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,a.jsx)(h.Z,{numColSpan:2,children:(0,a.jsxs)(L.Z,{children:[(0,a.jsx)(y.Z,{children:"Spend Per Tag"}),(0,a.jsxs)(_.Z,{children:["Get Started Tracking cost per tag ",(0,a.jsx)("a",{className:"text-blue-500",href:"https://docs.litellm.ai/docs/proxy/cost_tracking",target:"_blank",children:"here"})]}),(0,a.jsx)(ex.Z,{className:"h-72",data:w,index:"name",categories:["spend"],colors:["blue"]})]})}),(0,a.jsx)(h.Z,{numColSpan:2})]})]})]})]})})};let lp=e=>{if(e)return e.toISOString().split("T")[0]};function lj(e){return new Intl.NumberFormat("en-US",{maximumFractionDigits:0,notation:"compact",compactDisplay:"short"}).format(e)}var lg=e=>{let{accessToken:l,token:s,userRole:t,userID:n,premiumUser:i}=e,[o,d]=(0,r.useState)([]),[c,m]=(0,r.useState)([]),[p,j]=(0,r.useState)([]),[g,Z]=(0,r.useState)([]),[f,_]=(0,r.useState)("0"),[y,b]=(0,r.useState)("0"),[v,k]=(0,r.useState)("0"),[S,w]=(0,r.useState)({from:new Date(Date.now()-6048e5),to:new Date});(0,r.useEffect)(()=>{l&&S&&(async()=>{Z(await (0,u.zg)(l,lp(S.from),lp(S.to)))})()},[l]);let N=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.api_key)&&void 0!==l?l:""}))),I=Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.model)&&void 0!==l?l:""})));Array.from(new Set(g.map(e=>{var l;return null!==(l=null==e?void 0:e.call_type)&&void 0!==l?l:""})));let A=async(e,s)=>{e&&s&&l&&(s.setHours(23,59,59,999),e.setHours(0,0,0,0),Z(await (0,u.zg)(l,lp(e),lp(s))))};return(0,r.useEffect)(()=>{console.log("DATA IN CACHE DASHBOARD",g);let e=g;c.length>0&&(e=e.filter(e=>c.includes(e.api_key))),p.length>0&&(e=e.filter(e=>p.includes(e.model))),console.log("before processed data in cache dashboard",e);let l=0,s=0,t=0,n=e.reduce((e,n)=>{console.log("Processing item:",n),n.call_type||(console.log("Item has no call_type:",n),n.call_type="Unknown"),l+=(n.total_rows||0)-(n.cache_hit_true_rows||0),s+=n.cache_hit_true_rows||0,t+=n.cached_completion_tokens||0;let a=e.find(e=>e.name===n.call_type);return a?(a["LLM API requests"]+=(n.total_rows||0)-(n.cache_hit_true_rows||0),a["Cache hit"]+=n.cache_hit_true_rows||0,a["Cached Completion Tokens"]+=n.cached_completion_tokens||0,a["Generated Completion Tokens"]+=n.generated_completion_tokens||0):e.push({name:n.call_type,"LLM API requests":(n.total_rows||0)-(n.cache_hit_true_rows||0),"Cache hit":n.cache_hit_true_rows||0,"Cached Completion Tokens":n.cached_completion_tokens||0,"Generated Completion Tokens":n.generated_completion_tokens||0}),e},[]);_(lj(s)),b(lj(t));let a=s+l;a>0?k((s/a*100).toFixed(2)):k("0"),d(n),console.log("PROCESSED DATA IN CACHE DASHBOARD",n)},[c,p,S,g]),(0,a.jsxs)(L.Z,{children:[(0,a.jsxs)(x.Z,{numItems:3,className:"gap-4 mt-4",children:[(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select API Keys",value:c,onValueChange:m,children:N.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(em.Z,{placeholder:"Select Models",value:p,onValueChange:j,children:I.map(e=>(0,a.jsx)(eu.Z,{value:e,children:e},e))})}),(0,a.jsx)(h.Z,{children:(0,a.jsx)(ea.Z,{enableSelect:!0,value:S,onValueChange:e=>{w(e),A(e.from,e.to)},selectPlaceholder:"Select date range"})})]}),(0,a.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mt-4",children:[(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hit Ratio"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsxs)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:[v,"%"]})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cache Hits"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:f})})]}),(0,a.jsxs)(L.Z,{children:[(0,a.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Cached Tokens"}),(0,a.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,a.jsx)("p",{className:"text-tremor-metric font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:y})})]})]}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cache Hits vs API Requests"}),(0,a.jsx)(ex.Z,{title:"Cache Hits vs API Requests",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["LLM API requests","Cache hit"],colors:["sky","teal"],yAxisWidth:48}),(0,a.jsx)(en.Z,{className:"mt-4",children:"Cached Completion Tokens vs Generated Completion Tokens"}),(0,a.jsx)(ex.Z,{className:"mt-6",data:o,stack:!0,index:"name",valueFormatter:lj,categories:["Generated Completion Tokens","Cached Completion Tokens"],colors:["sky","teal"],yAxisWidth:48})]})},lZ=()=>{let{Title:e,Paragraph:l}=es.default,[s,t]=(0,r.useState)(""),[n,o]=(0,r.useState)(!1),[d,c]=(0,r.useState)(null),[h,x]=(0,r.useState)(null),[p,j]=(0,r.useState)(null),[g,Z]=(0,r.useState)({PROXY_BASE_URL:"",PROXY_LOGOUT_URL:""}),[f,_]=(0,r.useState)(!0),y=(0,i.useSearchParams)(),[b,v]=(0,r.useState)({data:[]}),k=y.get("userID"),S=y.get("invitation_id"),w=function(e){console.log("COOKIES",document.cookie);let l=document.cookie.split("; ").find(l=>l.startsWith(e+"="));return l?l.split("=")[1]:null}("token"),[N,I]=(0,r.useState)("api-keys"),[A,C]=(0,r.useState)(null);return(0,r.useEffect)(()=>{if(w){let e=(0,el.o)(w);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),C(e.key),e.user_role){let l=function(e){if(!e)return"Undefined Role";switch(console.log("Received user role: ".concat(e.toLowerCase())),console.log("Received user role length: ".concat(e.toLowerCase().length)),e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"internal_user":return"Internal User";case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),t(l),"Admin Viewer"==l&&I("usage")}else console.log("User role not defined");e.user_email?c(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?_("username_password"==e.login_method):console.log("User Email is not set ".concat(e)),e.premium_user&&o(e.premium_user),e.auth_header_name&&(0,u.K8)(e.auth_header_name)}}},[w]),(0,a.jsx)(r.Suspense,{fallback:(0,a.jsx)("div",{children:"Loading..."}),children:S?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):(0,a.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,a.jsx)(m,{userID:k,userRole:s,userEmail:d,premiumUser:n,setProxySettings:Z,proxySettings:g}),(0,a.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,a.jsx)("div",{className:"mt-8",children:(0,a.jsx)(lc,{setPage:I,userRole:s,defaultSelectedKey:null})}),"api-keys"==N?(0,a.jsx)(et,{userID:k,userRole:s,premiumUser:n,teams:h,keys:p,setUserRole:t,userEmail:d,setUserEmail:c,setTeams:x,setKeys:j}):"models"==N?(0,a.jsx)(eO,{userID:k,userRole:s,token:w,keys:p,accessToken:A,modelData:b,setModelData:v,premiumUser:n}):"llm-playground"==N?(0,a.jsx)(la,{userID:k,userRole:s,token:w,accessToken:A}):"users"==N?(0,a.jsx)(eL,{userID:k,userRole:s,token:w,keys:p,teams:h,accessToken:A,setKeys:j}):"teams"==N?(0,a.jsx)(eU,{teams:h,setTeams:x,searchParams:y,accessToken:A,userID:k,userRole:s}):"admin-panel"==N?(0,a.jsx)(eV,{setTeams:x,searchParams:y,accessToken:A,showSSOBanner:f,premiumUser:n}):"api_ref"==N?(0,a.jsx)(lt,{proxySettings:g}):"settings"==N?(0,a.jsx)(eJ,{userID:k,userRole:s,accessToken:A,premiumUser:n}):"budgets"==N?(0,a.jsx)(ll,{accessToken:A}):"general-settings"==N?(0,a.jsx)(e2,{userID:k,userRole:s,accessToken:A,modelData:b}):"model-hub"==N?(0,a.jsx)(ls.Z,{accessToken:A,publicPage:!1,premiumUser:n}):"caching"==N?(0,a.jsx)(lg,{userID:k,userRole:s,token:w,accessToken:A,premiumUser:n}):"pass-through-settings"==N?(0,a.jsx)(e7,{userID:k,userRole:s,accessToken:A,modelData:b}):(0,a.jsx)(lx,{userID:k,userRole:s,token:w,accessToken:A,keys:p,premiumUser:n})]})]})})}},41134:function(e,l,s){"use strict";s.d(l,{Z:function(){return y}});var t=s(57437),n=s(2265),a=s(47907),r=s(777),i=s(2179),o=s(13810),d=s(92836),c=s(26734),m=s(41608),u=s(32126),h=s(23682),x=s(71801),p=s(42440),j=s(84174),g=s(50459),Z=s(6180),f=s(99129),_=s(67951),y=e=>{var l;let{accessToken:s,publicPage:y,premiumUser:b}=e,[v,k]=(0,n.useState)(!1),[S,w]=(0,n.useState)(null),[N,I]=(0,n.useState)(!1),[A,C]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),E=(0,a.useRouter)();(0,n.useEffect)(()=>{s&&(async()=>{try{let e=await (0,r.kn)(s);console.log("ModelHubData:",e),w(e.data),(0,r.E9)(s,"enable_public_model_hub").then(e=>{console.log("data: ".concat(JSON.stringify(e))),!0==e.field_value&&k(!0)}).catch(e=>{})}catch(e){console.error("There was an error fetching the model data",e)}})()},[s,y]);let O=e=>{T(e),I(!0)},R=async()=>{s&&(0,r.jA)(s,"enable_public_model_hub",!0).then(e=>{C(!0)})},F=()=>{I(!1),C(!1),T(null)},M=()=>{I(!1),C(!1),T(null)},D=e=>{navigator.clipboard.writeText(e)};return(0,t.jsxs)("div",{children:[y&&v||!1==y?(0,t.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,t.jsx)("div",{className:"relative w-full"}),(0,t.jsxs)("div",{className:"flex ".concat(y?"justify-between":"items-center"),children:[(0,t.jsx)(p.Z,{className:"ml-8 text-center ",children:"Model Hub"}),!1==y?b?(0,t.jsx)(i.Z,{className:"ml-4",onClick:()=>R(),children:"✨ Make Public"}):(0,t.jsx)(i.Z,{className:"ml-4",children:(0,t.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"✨ Make Public"})}):(0,t.jsxs)("div",{className:"flex justify-between items-center",children:[(0,t.jsx)("p",{children:"Filter by key:"}),(0,t.jsx)(x.Z,{className:"bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center",children:"/ui/model_hub?key="})]})]}),(0,t.jsx)("div",{className:"grid grid-cols-2 gap-6 sm:grid-cols-3 lg:grid-cols-4 pr-8",children:S&&S.map(e=>(0,t.jsxs)(o.Z,{className:"mt-5 mx-8",children:[(0,t.jsxs)("pre",{className:"flex justify-between",children:[(0,t.jsx)(p.Z,{children:e.model_group}),(0,t.jsx)(Z.Z,{title:e.model_group,children:(0,t.jsx)(j.Z,{onClick:()=>D(e.model_group),style:{cursor:"pointer",marginRight:"10px"}})})]}),(0,t.jsxs)("div",{className:"my-5",children:[(0,t.jsxs)(x.Z,{children:["Mode: ",e.mode]}),(0,t.jsxs)(x.Z,{children:["Supports Function Calling:"," ",(null==e?void 0:e.supports_function_calling)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Supports Vision:"," ",(null==e?void 0:e.supports_vision)==!0?"Yes":"No"]}),(0,t.jsxs)(x.Z,{children:["Max Input Tokens:"," ",(null==e?void 0:e.max_input_tokens)?null==e?void 0:e.max_input_tokens:"N/A"]}),(0,t.jsxs)(x.Z,{children:["Max Output Tokens:"," ",(null==e?void 0:e.max_output_tokens)?null==e?void 0:e.max_output_tokens:"N/A"]})]}),(0,t.jsx)("div",{style:{marginTop:"auto",textAlign:"right"},children:(0,t.jsxs)("a",{href:"#",onClick:()=>O(e),style:{color:"#1890ff",fontSize:"smaller"},children:["View more ",(0,t.jsx)(g.Z,{})]})})]},e.model_group))})]}):(0,t.jsxs)(o.Z,{className:"mx-auto max-w-xl mt-10",children:[(0,t.jsx)(x.Z,{className:"text-xl text-center mb-2 text-black",children:"Public Model Hub not enabled."}),(0,t.jsx)("p",{className:"text-base text-center text-slate-800",children:"Ask your proxy admin to enable this on their Admin UI."})]}),(0,t.jsx)(f.Z,{title:"Public Model Hub",width:600,visible:A,footer:null,onOk:F,onCancel:M,children:(0,t.jsxs)("div",{className:"pt-5 pb-5",children:[(0,t.jsxs)("div",{className:"flex justify-between mb-4",children:[(0,t.jsx)(x.Z,{className:"text-base mr-2",children:"Shareable Link:"}),(0,t.jsx)(x.Z,{className:"max-w-sm ml-2 bg-gray-200 pr-2 pl-2 pt-1 pb-1 text-center rounded",children:"/ui/model_hub?key="})]}),(0,t.jsx)("div",{className:"flex justify-end",children:(0,t.jsx)(i.Z,{onClick:()=>{E.replace("/model_hub?key=".concat(s))},children:"See Page"})})]})}),(0,t.jsx)(f.Z,{title:P&&P.model_group?P.model_group:"Unknown Model",width:800,visible:N,footer:null,onOk:F,onCancel:M,children:P&&(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-4",children:(0,t.jsx)("strong",{children:"Model Information & Usage"})}),(0,t.jsxs)(c.Z,{children:[(0,t.jsxs)(m.Z,{children:[(0,t.jsx)(d.Z,{children:"OpenAI Python SDK"}),(0,t.jsx)(d.Z,{children:"Supported OpenAI Params"}),(0,t.jsx)(d.Z,{children:"LlamaIndex"}),(0,t.jsx)(d.Z,{children:"Langchain Py"})]}),(0,t.jsxs)(h.Z,{children:[(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport openai\nclient = openai.OpenAI(\n api_key="your_api_key",\n base_url="http://0.0.0.0:4000" # LiteLLM Proxy is OpenAI compatible, Read More: https://docs.litellm.ai/docs/proxy/user_keys\n)\n\nresponse = client.chat.completions.create(\n model="'.concat(P.model_group,'", # model to send to the proxy\n messages = [\n {\n "role": "user",\n "content": "this is a test request, write a short poem"\n }\n ]\n)\n\nprint(response)\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:"".concat(null===(l=P.supported_openai_params)||void 0===l?void 0:l.map(e=>"".concat(e,"\n")).join(""))})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nimport os, dotenv\n\nfrom llama_index.llms import AzureOpenAI\nfrom llama_index.embeddings import AzureOpenAIEmbedding\nfrom llama_index import VectorStoreIndex, SimpleDirectoryReader, ServiceContext\n\nllm = AzureOpenAI(\n engine="'.concat(P.model_group,'", # model_name on litellm proxy\n temperature=0.0,\n azure_endpoint="http://0.0.0.0:4000", # litellm proxy endpoint\n api_key="sk-1234", # litellm proxy API Key\n api_version="2023-07-01-preview",\n)\n\nembed_model = AzureOpenAIEmbedding(\n deployment_name="azure-embedding-model",\n azure_endpoint="http://0.0.0.0:4000",\n api_key="sk-1234",\n api_version="2023-07-01-preview",\n)\n\n\ndocuments = SimpleDirectoryReader("llama_index_data").load_data()\nservice_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model)\nindex = VectorStoreIndex.from_documents(documents, service_context=service_context)\n\nquery_engine = index.as_query_engine()\nresponse = query_engine.query("What did the author do growing up?")\nprint(response)\n\n ')})}),(0,t.jsx)(u.Z,{children:(0,t.jsx)(_.Z,{language:"python",children:'\nfrom langchain.chat_models import ChatOpenAI\nfrom langchain.prompts.chat import (\n ChatPromptTemplate,\n HumanMessagePromptTemplate,\n SystemMessagePromptTemplate,\n)\nfrom langchain.schema import HumanMessage, SystemMessage\n\nchat = ChatOpenAI(\n openai_api_base="http://0.0.0.0:4000",\n model = "'.concat(P.model_group,'",\n temperature=0.1\n)\n\nmessages = [\n SystemMessage(\n content="You are a helpful assistant that im using to make a test request to."\n ),\n HumanMessage(\n content="test from litellm. tell me why it\'s amazing in 1 sentence"\n ),\n]\nresponse = chat(messages)\n\nprint(response)\n\n ')})})]})]})]})})]})}}},function(e){e.O(0,[665,936,902,131,684,626,777,971,69,744],function(){return e(e.s=20661)}),_N_E=e.O()}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/Q5YcBgN0qLD3pcZcx1fRm/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/ffXp7j1jzMKpweBFKW_w2/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/index.html b/ui/litellm-dashboard/out/index.html index 2f7b4e28c7..c482cdd956 100644 --- a/ui/litellm-dashboard/out/index.html +++ b/ui/litellm-dashboard/out/index.html @@ -1 +1 @@ -LiteLLM Dashboard \ No newline at end of file +LiteLLM Dashboard \ No newline at end of file diff --git a/ui/litellm-dashboard/out/index.txt b/ui/litellm-dashboard/out/index.txt index 296e848453..7bd7e345de 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[68031,["665","static/chunks/3014691f-b24e8254c7593934.js","936","static/chunks/2f6dbc85-cac2949a76539886.js","902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","684","static/chunks/684-16b194c83a169f6d.js","626","static/chunks/626-fc3969bfc35ead00.js","777","static/chunks/777-a81b45dec53652df.js","931","static/chunks/app/page-7c218fb97a2a9817.js"],""] +3:I[68031,["665","static/chunks/3014691f-b24e8254c7593934.js","936","static/chunks/2f6dbc85-cac2949a76539886.js","902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","684","static/chunks/684-16b194c83a169f6d.js","626","static/chunks/626-fc3969bfc35ead00.js","777","static/chunks/777-a81b45dec53652df.js","931","static/chunks/app/page-7b75dc53f1c6e449.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/ui/litellm-dashboard/out/model_hub.html b/ui/litellm-dashboard/out/model_hub.html index bc65f3d703..bbf4caf53e 100644 --- a/ui/litellm-dashboard/out/model_hub.html +++ b/ui/litellm-dashboard/out/model_hub.html @@ -1 +1 @@ -LiteLLM Dashboard \ No newline at end of file +LiteLLM Dashboard \ No newline at end of file diff --git a/ui/litellm-dashboard/out/model_hub.txt b/ui/litellm-dashboard/out/model_hub.txt index 6de8880d9b..e95d7b6b45 100644 --- a/ui/litellm-dashboard/out/model_hub.txt +++ b/ui/litellm-dashboard/out/model_hub.txt @@ -2,6 +2,6 @@ 3:I[87494,["902","static/chunks/902-58bf23027703b2e8.js","131","static/chunks/131-3d2257b0ff5aadb2.js","777","static/chunks/777-a81b45dec53652df.js","418","static/chunks/app/model_hub/page-8ed460f3f33c0bf2.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null diff --git a/ui/litellm-dashboard/out/onboarding.html b/ui/litellm-dashboard/out/onboarding.html index 9ee6afdcbf..82b9b495fd 100644 --- a/ui/litellm-dashboard/out/onboarding.html +++ b/ui/litellm-dashboard/out/onboarding.html @@ -1 +1 @@ -LiteLLM Dashboard \ No newline at end of file +LiteLLM Dashboard \ No newline at end of file diff --git a/ui/litellm-dashboard/out/onboarding.txt b/ui/litellm-dashboard/out/onboarding.txt index 38e7f4b555..fc2ff8abeb 100644 --- a/ui/litellm-dashboard/out/onboarding.txt +++ b/ui/litellm-dashboard/out/onboarding.txt @@ -2,6 +2,6 @@ 3:I[667,["665","static/chunks/3014691f-b24e8254c7593934.js","902","static/chunks/902-58bf23027703b2e8.js","684","static/chunks/684-16b194c83a169f6d.js","777","static/chunks/777-a81b45dec53652df.js","461","static/chunks/app/onboarding/page-cba59362096ed469.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["Q5YcBgN0qLD3pcZcx1fRm",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["ffXp7j1jzMKpweBFKW_w2",[[["",{"children":["onboarding",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["onboarding",{"children":["__PAGE__",{},["$L1",["$","$L2",null,{"propsForComponent":{"params":{}},"Component":"$3","isStaticGeneration":true}],null]]},["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","onboarding","children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","styles":null}]]},[null,["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_86ef86","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"loading":"$undefined","loadingStyles":"$undefined","loadingScripts":"$undefined","hasLoading":false,"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[],"styles":null}]}]}],null]],[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/00256a1984d35914.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] 6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]] 1:null From 38708a355a066027a5d251b6199fc70946c62157 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 25 Oct 2024 23:39:15 +0400 Subject: [PATCH 57/62] =?UTF-8?q?bump:=20version=201.50.4=20=E2=86=92=201.?= =?UTF-8?q?51.0?= 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 68b5f78bda..a9dde2bfcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.50.4" +version = "1.51.0" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -91,7 +91,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.50.4" +version = "1.51.0" version_files = [ "pyproject.toml:^version" ] From c03e5da41ff4e8f112a02849fa2616696ae9b182 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Fri, 25 Oct 2024 15:55:56 -0700 Subject: [PATCH 58/62] LiteLLM Minor Fixes & Improvements (10/24/2024) (#6421) * fix(utils.py): support passing dynamic api base to validate_environment Returns True if just api base is required and api base is passed * fix(litellm_pre_call_utils.py): feature flag sending client headers to llm api Fixes https://github.com/BerriAI/litellm/issues/6410 * fix(anthropic/chat/transformation.py): return correct error message * fix(http_handler.py): add error response text in places where we expect it * fix(factory.py): handle base case of no non-system messages to bedrock Fixes https://github.com/BerriAI/litellm/issues/6411 * feat(cohere/embed): Support cohere image embeddings Closes https://github.com/BerriAI/litellm/issues/6413 * fix(__init__.py): fix linting error * docs(supported_embedding.md): add image embedding example to docs * feat(cohere/embed): use cohere embedding returned usage for cost calc * build(model_prices_and_context_window.json): add embed-english-v3.0 details (image cost + 'supports_image_input' flag) * fix(cohere_transformation.py): fix linting error * test(test_proxy_server.py): cleanup test * test: cleanup test * fix: fix linting errors --- .../docs/embedding/supported_embedding.md | 54 ++++++ docs/my-website/docs/proxy/configs.md | 1 + litellm/__init__.py | 3 +- .../get_llm_provider_logic.py | 8 + litellm/llms/anthropic/chat/handler.py | 2 + litellm/llms/azure_ai/embed/handler.py | 2 +- .../bedrock/embed/cohere_transformation.py | 21 ++- litellm/llms/bedrock/embed/embedding.py | 4 +- .../cohere/{embed.py => embed/handler.py} | 89 ++-------- litellm/llms/cohere/embed/transformation.py | 160 ++++++++++++++++++ litellm/llms/custom_httpx/http_handler.py | 2 + litellm/llms/prompt_templates/factory.py | 9 + litellm/main.py | 2 +- ...odel_prices_and_context_window_backup.json | 38 +++-- litellm/proxy/litellm_pre_call_utils.py | 14 +- litellm/types/llms/bedrock.py | 16 +- litellm/utils.py | 20 ++- model_prices_and_context_window.json | 38 +++-- tests/local_testing/test_embedding.py | 25 +++ tests/local_testing/test_get_llm_provider.py | 9 + tests/local_testing/test_prompt_factory.py | 12 ++ tests/local_testing/test_proxy_server.py | 31 ++-- tests/local_testing/test_utils.py | 7 + 23 files changed, 417 insertions(+), 150 deletions(-) rename litellm/llms/cohere/{embed.py => embed/handler.py} (68%) create mode 100644 litellm/llms/cohere/embed/transformation.py diff --git a/docs/my-website/docs/embedding/supported_embedding.md b/docs/my-website/docs/embedding/supported_embedding.md index aa3c2c4c59..5250ea403d 100644 --- a/docs/my-website/docs/embedding/supported_embedding.md +++ b/docs/my-website/docs/embedding/supported_embedding.md @@ -84,6 +84,60 @@ print(query_result[:5]) + +## Image Embeddings + +For models that support image embeddings, you can pass in a base64 encoded image string to the `input` param. + + + + +```python +from litellm import embedding +import os + +# set your api key +os.environ["COHERE_API_KEY"] = "" + +response = embedding(model="cohere/embed-english-v3.0", input=[""]) +``` + + + + +1. Setup config.yaml + +```yaml +model_list: + - model_name: cohere-embed + litellm_params: + model: cohere/embed-english-v3.0 + api_key: os.environ/COHERE_API_KEY +``` + + +2. Start proxy + +```bash +litellm --config /path/to/config.yaml + +# RUNNING on http://0.0.0.0:4000 +``` + +3. Test it! + +```bash +curl -X POST 'http://0.0.0.0:4000/v1/embeddings' \ +-H 'Authorization: Bearer sk-54d77cd67b9febbb' \ +-H 'Content-Type: application/json' \ +-d '{ + "model": "cohere/embed-english-v3.0", + "input": [""] +}' +``` + + + ## Input Params for `litellm.embedding()` diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index bf16a96e66..ee9a9096fa 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -814,6 +814,7 @@ general_settings: | pass_through_endpoints | List[Dict[str, Any]] | Define the pass through endpoints. [Docs](./pass_through) | | enable_oauth2_proxy_auth | boolean | (Enterprise Feature) If true, enables oauth2.0 authentication | | forward_openai_org_id | boolean | If true, forwards the OpenAI Organization ID to the backend LLM call (if it's OpenAI). | +| forward_client_headers_to_llm_api | boolean | If true, forwards the client headers (any `x-` headers) to the backend LLM call | ### router_settings - Reference diff --git a/litellm/__init__.py b/litellm/__init__.py index 3282660e98..b1033e7a49 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -8,6 +8,7 @@ import os from typing import Callable, List, Optional, Dict, Union, Any, Literal, get_args from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.caching.caching import Cache, DualCache, RedisCache, InMemoryCache +from litellm.types.llms.bedrock import COHERE_EMBEDDING_INPUT_TYPES from litellm._logging import ( set_verbose, _turn_on_debug, @@ -136,7 +137,7 @@ enable_azure_ad_token_refresh: Optional[bool] = False ### DEFAULT AZURE API VERSION ### AZURE_DEFAULT_API_VERSION = "2024-08-01-preview" # this is updated to the latest ### COHERE EMBEDDINGS DEFAULT TYPE ### -COHERE_DEFAULT_EMBEDDING_INPUT_TYPE = "search_document" +COHERE_DEFAULT_EMBEDDING_INPUT_TYPE: COHERE_EMBEDDING_INPUT_TYPES = "search_document" ### GUARDRAILS ### llamaguard_model_name: Optional[str] = None openai_moderations_model_name: Optional[str] = None diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py index fad01f0fff..4b64fb828a 100644 --- a/litellm/litellm_core_utils/get_llm_provider_logic.py +++ b/litellm/litellm_core_utils/get_llm_provider_logic.py @@ -333,6 +333,14 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915 api_key: Optional[str], dynamic_api_key: Optional[str], ) -> Tuple[str, str, Optional[str], Optional[str]]: + """ + Returns: + Tuple[str, str, Optional[str], Optional[str]]: + model: str + custom_llm_provider: str + dynamic_api_key: Optional[str] + api_base: Optional[str] + """ custom_llm_provider = model.split("/", 1)[0] model = model.split("/", 1)[1] diff --git a/litellm/llms/anthropic/chat/handler.py b/litellm/llms/anthropic/chat/handler.py index 25cdcc2f3f..7deb5490db 100644 --- a/litellm/llms/anthropic/chat/handler.py +++ b/litellm/llms/anthropic/chat/handler.py @@ -398,6 +398,8 @@ class AnthropicChatCompletion(BaseLLM): error_response = getattr(e, "response", None) if error_headers is None and error_response: error_headers = getattr(error_response, "headers", None) + if error_response and hasattr(error_response, "text"): + error_text = getattr(error_response, "text", error_text) raise AnthropicError( message=error_text, status_code=status_code, diff --git a/litellm/llms/azure_ai/embed/handler.py b/litellm/llms/azure_ai/embed/handler.py index 682e7e6549..638a77479b 100644 --- a/litellm/llms/azure_ai/embed/handler.py +++ b/litellm/llms/azure_ai/embed/handler.py @@ -9,7 +9,7 @@ import httpx from openai import OpenAI import litellm -from litellm.llms.cohere.embed import embedding as cohere_embedding +from litellm.llms.cohere.embed.handler import embedding as cohere_embedding from litellm.llms.custom_httpx.http_handler import ( AsyncHTTPHandler, HTTPHandler, diff --git a/litellm/llms/bedrock/embed/cohere_transformation.py b/litellm/llms/bedrock/embed/cohere_transformation.py index 7a1ab75fd8..1020aa9230 100644 --- a/litellm/llms/bedrock/embed/cohere_transformation.py +++ b/litellm/llms/bedrock/embed/cohere_transformation.py @@ -7,6 +7,7 @@ Why separate file? Make it easy to see how transformation works from typing import List import litellm +from litellm.llms.cohere.embed.transformation import CohereEmbeddingConfig from litellm.types.llms.bedrock import CohereEmbeddingRequest, CohereEmbeddingResponse from litellm.types.utils import Embedding, EmbeddingResponse @@ -26,15 +27,21 @@ class BedrockCohereEmbeddingConfig: optional_params["embedding_types"] = v return optional_params + def _is_v3_model(self, model: str) -> bool: + return "3" in model + def _transform_request( - self, input: List[str], inference_params: dict + self, model: str, input: List[str], inference_params: dict ) -> CohereEmbeddingRequest: - transformed_request = CohereEmbeddingRequest( - texts=input, - input_type=litellm.COHERE_DEFAULT_EMBEDDING_INPUT_TYPE, # type: ignore + transformed_request = CohereEmbeddingConfig()._transform_request( + model, input, inference_params ) - for k, v in inference_params.items(): - transformed_request[k] = v # type: ignore + new_transformed_request = CohereEmbeddingRequest( + input_type=transformed_request["input_type"], + ) + for k in CohereEmbeddingRequest.__annotations__.keys(): + if k in transformed_request: + new_transformed_request[k] = transformed_request[k] # type: ignore - return transformed_request + return new_transformed_request diff --git a/litellm/llms/bedrock/embed/embedding.py b/litellm/llms/bedrock/embed/embedding.py index 6aefe20409..7a8591a94a 100644 --- a/litellm/llms/bedrock/embed/embedding.py +++ b/litellm/llms/bedrock/embed/embedding.py @@ -11,7 +11,7 @@ from typing import Any, Callable, List, Literal, Optional, Tuple, Union import httpx import litellm -from litellm.llms.cohere.embed import embedding as cohere_embedding +from litellm.llms.cohere.embed.handler import embedding as cohere_embedding from litellm.llms.custom_httpx.http_handler import ( AsyncHTTPHandler, HTTPHandler, @@ -369,7 +369,7 @@ class BedrockEmbedding(BaseAWSLLM): batch_data: Optional[List] = None if provider == "cohere": data = BedrockCohereEmbeddingConfig()._transform_request( - input=input, inference_params=inference_params + model=model, input=input, inference_params=inference_params ) elif provider == "amazon" and model in [ "amazon.titan-embed-image-v1", diff --git a/litellm/llms/cohere/embed.py b/litellm/llms/cohere/embed/handler.py similarity index 68% rename from litellm/llms/cohere/embed.py rename to litellm/llms/cohere/embed/handler.py index 5d640b506f..95cbec2257 100644 --- a/litellm/llms/cohere/embed.py +++ b/litellm/llms/cohere/embed/handler.py @@ -12,8 +12,11 @@ import requests # type: ignore import litellm from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler +from litellm.types.llms.bedrock import CohereEmbeddingRequest from litellm.utils import Choices, Message, ModelResponse, Usage +from .transformation import CohereEmbeddingConfig + def validate_environment(api_key, headers: dict): headers.update( @@ -41,39 +44,9 @@ class CohereError(Exception): ) # Call the base class constructor with the parameters it needs -def _process_embedding_response( - embeddings: list, - model_response: litellm.EmbeddingResponse, - model: str, - encoding: Any, - input: list, -) -> litellm.EmbeddingResponse: - output_data = [] - for idx, embedding in enumerate(embeddings): - output_data.append( - {"object": "embedding", "index": idx, "embedding": embedding} - ) - model_response.object = "list" - model_response.data = output_data - model_response.model = model - input_tokens = 0 - for text in input: - input_tokens += len(encoding.encode(text)) - - setattr( - model_response, - "usage", - Usage( - prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens - ), - ) - - return model_response - - async def async_embedding( model: str, - data: dict, + data: Union[dict, CohereEmbeddingRequest], input: list, model_response: litellm.utils.EmbeddingResponse, timeout: Optional[Union[float, httpx.Timeout]], @@ -121,19 +94,12 @@ async def async_embedding( ) raise e - ## LOGGING - logging_obj.post_call( - input=input, - api_key=api_key, - additional_args={"complete_input_dict": data}, - original_response=response.text, - ) - - embeddings = response.json()["embeddings"] - ## PROCESS RESPONSE ## - return _process_embedding_response( - embeddings=embeddings, + return CohereEmbeddingConfig()._transform_response( + response=response, + api_key=api_key, + logging_obj=logging_obj, + data=data, model_response=model_response, model=model, encoding=encoding, @@ -149,7 +115,7 @@ def embedding( optional_params: dict, headers: dict, encoding: Any, - data: Optional[dict] = None, + data: Optional[Union[dict, CohereEmbeddingRequest]] = None, complete_api_base: Optional[str] = None, api_key: Optional[str] = None, aembedding: Optional[bool] = None, @@ -159,11 +125,10 @@ def embedding( headers = validate_environment(api_key, headers=headers) embed_url = complete_api_base or "https://api.cohere.ai/v1/embed" model = model - data = data or {"model": model, "texts": input, **optional_params} - if "3" in model and "input_type" not in data: - # cohere v3 embedding models require input_type, if no input_type is provided, default to "search_document" - data["input_type"] = "search_document" + data = data or CohereEmbeddingConfig()._transform_request( + model=model, input=input, inference_params=optional_params + ) ## ROUTING if aembedding is True: @@ -193,30 +158,12 @@ def embedding( client = HTTPHandler(concurrent_limit=1) response = client.post(embed_url, headers=headers, data=json.dumps(data)) - ## LOGGING - logging_obj.post_call( - input=input, - api_key=api_key, - additional_args={"complete_input_dict": data}, - original_response=response, - ) - """ - response - { - 'object': "list", - 'data': [ - - ] - 'model', - 'usage' - } - """ - if response.status_code != 200: - raise CohereError(message=response.text, status_code=response.status_code) - embeddings = response.json()["embeddings"] - return _process_embedding_response( - embeddings=embeddings, + return CohereEmbeddingConfig()._transform_response( + response=response, + api_key=api_key, + logging_obj=logging_obj, + data=data, model_response=model_response, model=model, encoding=encoding, diff --git a/litellm/llms/cohere/embed/transformation.py b/litellm/llms/cohere/embed/transformation.py new file mode 100644 index 0000000000..e6bb0f392a --- /dev/null +++ b/litellm/llms/cohere/embed/transformation.py @@ -0,0 +1,160 @@ +""" +Transformation logic from OpenAI /v1/embeddings format to Cohere's /v1/embed format. + +Why separate file? Make it easy to see how transformation works + +Convers +- v3 embedding models +- v2 embedding models + +Docs - https://docs.cohere.com/v2/reference/embed +""" + +import types +from typing import Any, List, Optional, Union + +import httpx + +from litellm import COHERE_DEFAULT_EMBEDDING_INPUT_TYPE +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.types.llms.bedrock import ( + COHERE_EMBEDDING_INPUT_TYPES, + CohereEmbeddingRequest, + CohereEmbeddingRequestWithModel, +) +from litellm.types.utils import ( + Embedding, + EmbeddingResponse, + PromptTokensDetailsWrapper, + Usage, +) +from litellm.utils import is_base64_encoded + + +class CohereEmbeddingConfig: + """ + Reference: https://docs.cohere.com/v2/reference/embed + """ + + def __init__(self) -> None: + pass + + def get_supported_openai_params(self) -> List[str]: + return ["encoding_format"] + + def map_openai_params( + self, non_default_params: dict, optional_params: dict + ) -> dict: + for k, v in non_default_params.items(): + if k == "encoding_format": + optional_params["embedding_types"] = v + return optional_params + + def _is_v3_model(self, model: str) -> bool: + return "3" in model + + def _transform_request( + self, model: str, input: List[str], inference_params: dict + ) -> CohereEmbeddingRequestWithModel: + is_encoded = False + for input_str in input: + is_encoded = is_base64_encoded(input_str) + + if is_encoded: # check if string is b64 encoded image or not + transformed_request = CohereEmbeddingRequestWithModel( + model=model, + images=input, + input_type="image", + ) + else: + transformed_request = CohereEmbeddingRequestWithModel( + model=model, + texts=input, + input_type=COHERE_DEFAULT_EMBEDDING_INPUT_TYPE, + ) + + for k, v in inference_params.items(): + transformed_request[k] = v # type: ignore + + return transformed_request + + def _calculate_usage(self, input: List[str], encoding: Any, meta: dict) -> Usage: + + input_tokens = 0 + + text_tokens: Optional[int] = meta.get("billed_units", {}).get("input_tokens") + + image_tokens: Optional[int] = meta.get("billed_units", {}).get("images") + + prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None + if image_tokens is None and text_tokens is None: + for text in input: + input_tokens += len(encoding.encode(text)) + else: + prompt_tokens_details = PromptTokensDetailsWrapper( + image_tokens=image_tokens, + text_tokens=text_tokens, + ) + if image_tokens: + input_tokens += image_tokens + if text_tokens: + input_tokens += text_tokens + + return Usage( + prompt_tokens=input_tokens, + completion_tokens=0, + total_tokens=input_tokens, + prompt_tokens_details=prompt_tokens_details, + ) + + def _transform_response( + self, + response: httpx.Response, + api_key: Optional[str], + logging_obj: LiteLLMLoggingObj, + data: Union[dict, CohereEmbeddingRequest], + model_response: EmbeddingResponse, + model: str, + encoding: Any, + input: list, + ) -> EmbeddingResponse: + + response_json = response.json() + ## LOGGING + logging_obj.post_call( + input=input, + api_key=api_key, + additional_args={"complete_input_dict": data}, + original_response=response_json, + ) + """ + response + { + 'object': "list", + 'data': [ + + ] + 'model', + 'usage' + } + """ + embeddings = response_json["embeddings"] + output_data = [] + for idx, embedding in enumerate(embeddings): + output_data.append( + {"object": "embedding", "index": idx, "embedding": embedding} + ) + model_response.object = "list" + model_response.data = output_data + model_response.model = model + input_tokens = 0 + for text in input: + input_tokens += len(encoding.encode(text)) + + setattr( + model_response, + "usage", + self._calculate_usage(input, encoding, response_json.get("meta", {})), + ) + + return model_response diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index 89b294584e..55851a6365 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -152,8 +152,10 @@ class AsyncHTTPHandler: setattr(e, "status_code", e.response.status_code) if stream is True: setattr(e, "message", await e.response.aread()) + setattr(e, "text", await e.response.aread()) else: setattr(e, "message", e.response.text) + setattr(e, "text", e.response.text) raise e except Exception as e: raise e diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index 15ee85faee..ebfdd41d09 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -2429,6 +2429,15 @@ def _bedrock_converse_messages_pt( # noqa: PLR0915 contents: List[BedrockMessageBlock] = [] msg_i = 0 + ## BASE CASE ## + if len(messages) == 0: + raise litellm.BadRequestError( + message=BAD_MESSAGE_ERROR_STR + + "bedrock requires at least one non-system message", + model=model, + llm_provider=llm_provider, + ) + # if initial message is assistant message if messages[0].get("role") is not None and messages[0]["role"] == "assistant": if user_continue_message is not None: diff --git a/litellm/main.py b/litellm/main.py index f239d2612c..f6680f2df8 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -113,7 +113,7 @@ from .llms.bedrock.chat import BedrockConverseLLM, BedrockLLM from .llms.bedrock.embed.embedding import BedrockEmbedding from .llms.cohere import chat as cohere_chat from .llms.cohere import completion as cohere_completion # type: ignore -from .llms.cohere import embed as cohere_embed +from .llms.cohere.embed import handler as cohere_embed from .llms.custom_llm import CustomLLM, custom_chat_llm_router from .llms.databricks.chat import DatabricksChatCompletion from .llms.groq.chat.handler import GroqChatCompletion diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 890ef86883..fe8834dbb4 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3364,54 +3364,56 @@ "litellm_provider": "cohere", "mode": "rerank" }, - "embed-english-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, - "input_cost_per_token": 0.00000010, - "output_cost_per_token": 0.00000, - "litellm_provider": "cohere", - "mode": "embedding" - }, "embed-english-light-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-multilingual-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-english-v2.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 4096, + "max_input_tokens": 4096, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-english-light-v2.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-multilingual-v2.0": { - "max_tokens": 256, - "max_input_tokens": 256, + "max_tokens": 768, + "max_input_tokens": 768, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, + "embed-english-v3.0": { + "max_tokens": 1024, + "max_input_tokens": 1024, + "input_cost_per_token": 0.00000010, + "input_cost_per_image": 0.0001, + "output_cost_per_token": 0.00000, + "litellm_provider": "cohere", + "mode": "embedding", + "supports_image_input": true + }, "replicate/meta/llama-2-13b": { "max_tokens": 4096, "max_input_tokens": 4096, diff --git a/litellm/proxy/litellm_pre_call_utils.py b/litellm/proxy/litellm_pre_call_utils.py index 9ee5476529..a34dffccdc 100644 --- a/litellm/proxy/litellm_pre_call_utils.py +++ b/litellm/proxy/litellm_pre_call_utils.py @@ -238,11 +238,15 @@ class LiteLLMProxyRequestSetup: - Adds org id """ data = LitellmDataForBackendLLMCall() - _headers = LiteLLMProxyRequestSetup.add_headers_to_llm_call( - headers, user_api_key_dict - ) - if _headers != {}: - data["headers"] = _headers + if ( + general_settings + and general_settings.get("forward_client_headers_to_llm_api") is True + ): + _headers = LiteLLMProxyRequestSetup.add_headers_to_llm_call( + headers, user_api_key_dict + ) + if _headers != {}: + data["headers"] = _headers _organization = LiteLLMProxyRequestSetup.get_openai_org_id_from_headers( headers, general_settings ) diff --git a/litellm/types/llms/bedrock.py b/litellm/types/llms/bedrock.py index 4fa0b06bb2..737aac3c31 100644 --- a/litellm/types/llms/bedrock.py +++ b/litellm/types/llms/bedrock.py @@ -210,15 +210,23 @@ class ServerSentEvent: return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" +COHERE_EMBEDDING_INPUT_TYPES = Literal[ + "search_document", "search_query", "classification", "clustering", "image" +] + + class CohereEmbeddingRequest(TypedDict, total=False): - texts: Required[List[str]] - input_type: Required[ - Literal["search_document", "search_query", "classification", "clustering"] - ] + texts: List[str] + images: List[str] + input_type: Required[COHERE_EMBEDDING_INPUT_TYPES] truncate: Literal["NONE", "START", "END"] embedding_types: Literal["float", "int8", "uint8", "binary", "ubinary"] +class CohereEmbeddingRequestWithModel(CohereEmbeddingRequest): + model: Required[str] + + class CohereEmbeddingResponse(TypedDict): embeddings: List[List[float]] id: str diff --git a/litellm/utils.py b/litellm/utils.py index dc190bc1a8..deb3ae8c63 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5197,7 +5197,9 @@ def create_proxy_transport_and_mounts(): def validate_environment( # noqa: PLR0915 - model: Optional[str] = None, api_key: Optional[str] = None + model: Optional[str] = None, + api_key: Optional[str] = None, + api_base: Optional[str] = None, ) -> dict: """ Checks if the environment variables are valid for the given model. @@ -5224,11 +5226,6 @@ def validate_environment( # noqa: PLR0915 _, custom_llm_provider, _, _ = get_llm_provider(model=model) except Exception: custom_llm_provider = None - # # check if llm provider part of model name - # if model.split("/",1)[0] in litellm.provider_list: - # custom_llm_provider = model.split("/", 1)[0] - # model = model.split("/", 1)[1] - # custom_llm_provider_passed_in = True if custom_llm_provider: if custom_llm_provider == "openai": @@ -5497,6 +5494,17 @@ def validate_environment( # noqa: PLR0915 if "api_key" not in key.lower(): new_missing_keys.append(key) missing_keys = new_missing_keys + + if api_base is not None: + new_missing_keys = [] + for key in missing_keys: + if "api_base" not in key.lower(): + new_missing_keys.append(key) + missing_keys = new_missing_keys + + if len(missing_keys) == 0: # no missing keys + keys_in_environment = True + return {"keys_in_environment": keys_in_environment, "missing_keys": missing_keys} diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 890ef86883..fe8834dbb4 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3364,54 +3364,56 @@ "litellm_provider": "cohere", "mode": "rerank" }, - "embed-english-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, - "input_cost_per_token": 0.00000010, - "output_cost_per_token": 0.00000, - "litellm_provider": "cohere", - "mode": "embedding" - }, "embed-english-light-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-multilingual-v3.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-english-v2.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 4096, + "max_input_tokens": 4096, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-english-light-v2.0": { - "max_tokens": 512, - "max_input_tokens": 512, + "max_tokens": 1024, + "max_input_tokens": 1024, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, "embed-multilingual-v2.0": { - "max_tokens": 256, - "max_input_tokens": 256, + "max_tokens": 768, + "max_input_tokens": 768, "input_cost_per_token": 0.00000010, "output_cost_per_token": 0.00000, "litellm_provider": "cohere", "mode": "embedding" }, + "embed-english-v3.0": { + "max_tokens": 1024, + "max_input_tokens": 1024, + "input_cost_per_token": 0.00000010, + "input_cost_per_image": 0.0001, + "output_cost_per_token": 0.00000, + "litellm_provider": "cohere", + "mode": "embedding", + "supports_image_input": true + }, "replicate/meta/llama-2-13b": { "max_tokens": 4096, "max_input_tokens": 4096, diff --git a/tests/local_testing/test_embedding.py b/tests/local_testing/test_embedding.py index 4c7560cccd..7993d3280d 100644 --- a/tests/local_testing/test_embedding.py +++ b/tests/local_testing/test_embedding.py @@ -1055,3 +1055,28 @@ def test_embedding_response_ratelimit_headers(model): assert int(additional_headers["x-ratelimit-remaining-requests"]) > 0 assert "x-ratelimit-remaining-tokens" in additional_headers assert int(additional_headers["x-ratelimit-remaining-tokens"]) > 0 + + +@pytest.mark.parametrize( + "input, input_type", + [ + ( + [ + "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD//gAfQ29tcHJlc3NlZCBieSBqcGVnLXJlY29tcHJlc3P/2wCEAAQEBAQEBAQEBAQGBgUGBggHBwcHCAwJCQkJCQwTDA4MDA4MExEUEA8QFBEeFxUVFx4iHRsdIiolJSo0MjRERFwBBAQEBAQEBAQEBAYGBQYGCAcHBwcIDAkJCQkJDBMMDgwMDgwTERQQDxAUER4XFRUXHiIdGx0iKiUlKjQyNEREXP/CABEIAZABkAMBIgACEQEDEQH/xAAdAAEAAQQDAQAAAAAAAAAAAAAABwEFBggCAwQJ/9oACAEBAAAAAN/gAAAAAAAAAAAAAAAAAAAAAAAAAAHTg9j6agAAp23/ADjsAAAPFrlAUYeagAAArdZ12uzcAAKax6jWUAAAAO/bna+oAC1aBxAAAAAAbM7rVABYvnRgYAAAAAbwbIABw+cMYAAAAAAvH1CuwA091RAAAAAAbpbPAGJfMXzAAAAAAJk+hdQGlmsQAAAAABk31JqBx+V1iAAAAAALp9W6gRp826AAAAAAGS/UqoGuGjwAAAAAAl76I1A1K1EAAAAAAG5G1ADUHU0AAAAAAu/1Cu4DVbTgAAAAAA3n2JAIG0IAAAAAArt3toAMV+XfEAAAAAL1uzPlQBT5qR2AAAAAenZDbm/AAa06SgAAAAerYra/LQADp+YmIAAAAC77J7Q5KAACIPnjwAAAAzbZzY24gAAGq+m4AAA7Zo2cmaoAAANWdOOAAAMl2N2TysAAAApEOj2HgAOyYtl5w5jw4zZPJyuGQ5H2AAAdes+suDUAVyfYbZTLajG8HxjgD153n3IAABH8QxxiVo4XPKpGlyTKjowvCbUAF4mD3AAACgqCzYPiPQAA900XAACmN4favRk+a9wB0xdiNAAAvU1cgAxeDcUoPdL0s1B44atQAACSs8AEewD0gM72I5jjDFiAAAPfO1QGL6z9IAlGdRgkaAAABMmRANZsSADls7k6kFW8AAAJIz4DHtW6AAk+d1jhUAAAGdyWBFcGgAX/AGnYZFgAAAM4k4CF4hAA9u3FcKi4AAAEiSEBCsRgAe3biuGxWAAACXsoAiKFgALttgs0J0AAAHpnvkBhOt4AGebE1pBtsAAAGeySA4an2wAGwEjGFxaAAAe+c+wAjKBgAyfZ3kUh3HAAAO6Yb+AKQLGgBctmb2HXDNjAAD1yzkQAENRF1gyvYG9AcI2wjgAByyuSveAAWWMcQtnoyOQs8qAPFhVh8HADt999y65gAAKKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/8QAGgEBAAMBAQEAAAAAAAAAAAAAAAEFBgIEA//aAAgBAhAAAAAAAAAAAAABEAAJkBEAAB0CIAABMhyAAA6EQAAA6EQAABMiIAAAmREAAAmQiAABMgOQAEyAHIATIACIBMu7H3fT419eACEnps7DoPFQch889Wd3V2TeWIBV0o+eF8I0OrXVoAIyvBm8uDe2Wp6ADO+Mw9WDV6rSgAzvjMNWA1Op1AARlvmZbOA3NnpfSAK6iHnwfnFttZ9Wh7AeXPcB5cxWd3Wk7Pvb+uR8q+rgAAAAAAAAP//EABsBAQABBQEAAAAAAAAAAAAAAAAEAQIDBQYH/9oACAEDEAAAAAAAAAC20AL6gCNDxAArnn3gpro4AAv2l4QIgAAJWwGLVAAAX7cQYYAAFdyNZgAAAy7UazAAABsZI18UAAE6YEfWgACRNygavCACsmZkALNZjAMkqVcAC2FFoKyJWe+fMyYoMAAUw2L8t0jYzqhE0dAzd70eHj+PK7mcAa7UDN7VvBwXmDb7EAU5uw9C9KCnh2n6WoAaKIey9ODy/jN+ADRRD2fpQeY8P0QAU5zGel+gg8V53oc4AgaYTfcJ45Tx5I31wCPobQ2PpPRYuP8APMZm2kqoxQddQAAAAAAAAP/EAFMQAAEDAgIDCQkMBwUIAwAAAAECAwQFEQAGBzFREhMhMEBBYXGBCBQYIjJCRlDSFSBSVGJygpGTobHREDRDc6LBwiMzU3CyFiQlNVVkdISSlLP/2gAIAQEAAT8A/wAo74nVaBAb32bNYitfDfcS2PrURiZpU0dwVFMjN1OVY8O8u7//APkFYc076LmfSVSvmQpB/ox4QGjH/r7v/wBGR7OPCA0YH0ge7IMj2ceEBowPpA92QZHs48IDRgfSB7sgyPZx4QGjA+kD3ZBkezjwgNGB9IHuyDI9nHhAaMD6QPdkGR7OPCA0YH0ge7IMj2ceEBowPpA92QZHs48IDRgfSB7sgyPZx4QGjA+kD3ZBkezjwgNGB9IHuyDI9nHhAaMD6QPdkGR7OPCA0YH0ge7IMj2ceEBowPpA92QZHs48IDRgfSB7sgyPZx4QGjA+kD3ZBkezjwgNGB9IHuyDI9nHhAaMD6QPdkGR7OPCA0Y89fd7IMj2cN6e9GDpCTmRaOuFI9nEDSlo9qakpj5upoJNgH3d4+50JxGlxpbSH4r7bzSvJW0sLSeop5NWsw0fL8RU2rVGPDjJ4C6+4EAnYnaegYzV3StDhFcfK1LdqDuoSZBLDHWlPlqxXtNmkOulaVVxcFg3/sYA73A+kLrxKnTJrpfmSXX3jrcdWVqPWVYudvJ7nbil16s0R7vikVSVDduCVR3lNk9e5IvjKfdG5rpKmo+Yo7NXi8ALlgxJH0kiysZL0l5Uzsz/AMFn2l7m7kJ8BuSj6PnAbU8ieeZitOPPuoQ22krWtZCUpSkXJJOoDGkHui4MBT1MyW2ibITdJnuA97o/dJ1uHFczFXMyzV1Gu1N+bJV57yr7kbEjUkdA5dGlSYb7UqJIcZfaUFtuNLKFoUNRSocIONF3dBb6tih58eSCQEM1PUOqT7eELS4lK0KCkkAgg3BB4/M2Z6NlKlSKtWJiI8VoWueFS1nUhA85ZxpJ0v13Pj7kNorg0NC7tw0K4XNi3yPKPRqHqLQnpkeoD8XKmZZJVSHCG4klw/qijqQs/wCF/pwDfjc1ZqpOUKNLrVXf3qMyLJSLFbrh8ltA51qxn7P9az9V1z6istxWypMSIhRLbCD+Kj5yvUYJHCMdz7pLXWoByfWJBXUILV4bizwvRk+Z0qa4yoTodKgyZ859DEWO0t11xZslCEC5UrGlHSNOz/XVvBa26RFKkQY+xHO4v5a/UtArU3LlZptbpzm4lQ30ut7DbWk9ChwHGXq5EzHQ6ZWoCv8AdpsdDyRrIKtaFdKTwHi+6I0hrffGRKU/ZloodqSkngW5rQz1I1n1P3M2ZzJpFYyvIXdUJ0SowP8AhP8AAtI6AvitIWbWclZVqlbWElxpvcRmz+0kOcDaf5nEyXJnypM2Y8p2Q+6t11xRupa1m6lHpJ9T6B6uaVpHo7alEMz0PQnepxN0/wASRgauJ7pTNZmVynZTjuXZpzYkSRtkPDgB6UI9UZMlrgZsy1MQqxZqkRy/QHRfA4iZIaiRX5D6ghpptTi1bEIFycZmrL2YcwVitvk7ubLdfsfNClcCewcHqiiX91qbbX3yz/rGBxGmKse4ujnMz6F2dfjiGj/2VBs/ccE3J9UZOirm5ry3EQm5eqkRu3Qp0YHEd01PLGUqPT0mxk1QLV0oZaPteqdBtKNV0kUIkXah77Md6mkcH8RGBq4jupH7JyXG/wDPcP1tj1T3MuWVMQK5mt9FjJWmDGO1tHjuHqJ4nupEnvrJa+beZ4/jR6ooNGnZhrFOotNa3yXMeS02OvWo9CRwk4ytQIeWKDS6HC/V4TCWgq1itWtSz0rPCeJ7qKNenZSl2/upEtonpcShXqcC+NA+jFeW4H+1NbYKatOaswysWMaOrbscc4rujaYZuj/vzccMCpR3yehwFn+r1MAVGwGNDOhVbK4ubc4xLLFnYMB1PCNjrw/BHF58opzDk7MlHSndOSID28ja6gbtH3jChZRHqShZerOZag1S6JT3pcpzUhsahtUTwJTtJxow0G0vKRYreYS1PrIAUhNrx4yvkA+WsfCONXFnGlTLZytnqvU5KLRlvmTG2Fl/xwB0J1eookOXPkNRYUZ1991W5baaQVrWdiUi5JxkbudKzVCzOzg+abE196NWXKWOnWlvGW8p0DKMEU6g01qKzwFe5F1uEDynFnhUeO7pTJ5n0aBmyK3d+mneJVtZjOnxVfQX6ghwZtRktQ4EV6RJcNkNMoK1qOwJTcnGTe5yr9V3qXmuSKXFNj3uizkpY/0oxlbIOVslRt6oVKaZdIst9XjyHPnOK4ezkFVgw6vAmU2ewHYsllbDiFaloWNyoYz1lKZknMtRoEu6gyvdMO8zrC/IXy2j0Cs5glpg0WmyJkk+YwgrIG1WwdJxk7uap75amZyqQit6zChkLe6lueSnGWcl5ayjGEegUliKCAFuAbp5z57irqPI9NOjVOdqB31T2x7tU5KlxNryNa2CenWnDra2XFtOoUhaFFKkqFiCOAgg8qyro7zdnJwCh0Z5xi9lSVje46etarA22DGUe5spEPe5ebqgue78Ui3aj9Sl+WvFIodHoMREGj02PDjJ1NMNhAJ2m2s8m07aIHJi5WdMsxSZFiuoxG08LoGt9sDz/hjGrkzLD0hxDLDSluLISlKQSpRPMAMZU0C54zFvcidHTR4Sv2k24dI+SyPG+u2MqaBskZc3qRLimrzEftZoBaB+S0PFw0y2y2hppCUIQAEpSAAAOYAauU6XtBJmuycy5LjASVXcl05sWDu1bGxe1GHWnGXFtOoUhxCilSVAghSTYgg6iOR5eyfmXNT/AHvQKNJmKBspTaLNo+es2SntOMq9zNIc3uTm+sBoazEgWWvtdWLDGWchZTyk2E0KiR4zlrKkEbt9XW4u6uW6SNDNAzwHZ7BTTq3YkSm0XS7sS+ka/na8ZuyJmbJMwxK9T1NJJs1IR47D3S2vj2mXXlobabUtaiAlKRcknUAMZV0F56zJvT8iEKVCVY77PuhZHyWvLxlTuesl0Te3qqlysy08JMnxI4PQ0n+onEWDFhMNxokdphhsWQ20gIQkbEpFgPeyqnBg/rMhCCBfc3ur6hw4lZ1hNbpMdlbpGokhKT+OHs7zVf3EdpHzgVfzGDnGqnnbHUkYGcqqOZo/OT+VsMZ5eBG/w0K2lJKPaxDzfTJBCXFLZUTbxk3+q2GJTEhAcYdQtB1KSoEckqdLp1ThvQqnEZkxXU7lbLyAtCusKxnPubKVNU9NyhOMB03Pekm7kfsXwqRjM+jfOWUVLNZochEcapLY31gj56LgduLHZxNjjL+TM0ZpcDdCokuWL2LiEWaSflOKskYyt3M8t0tSM31hLCNZiwbLc7XVCwxljR9lHKDaRQ6Kww6BZUlQ32Qr6a7nAAHvFLSkEqUAAMT81UyGClDm/r2N6u1WKhm2oywpDKt4bPMjX/8ALC3HHCVLWSSbm+338adLhuB2O+tChzg4pOdOFDVRRbm31A/EflhiQ1IbS6y4laFaik3HJCkKBBAII4RjMOibIOYCtc/LkZD6tb0W8Zy+0luwVisdzDRX925RMyS4uxMtlD46gUFGKj3NWdY11wajSpbf71bS/qUnErQTpPjXIy2Xk7WZLCv68L0R6R2/KylO+ikK/A4Tom0jL1ZRqHa3bEXQjpPlkBGVXkDa48yj8V4p/c358lEGW/TIaOcOSCtfYG0qxSO5gp6AldczQ+9tbhsBr+NwqxRNDWjygFDjGXmpL4N99nEyVH6K/FGGmGY7SGm20oQgAJSkAJAHMAPeyJ8WEjfJD6EX1XP4DWTioZ1ZRdEBndnmWvgT2DE6tVCoE98SFFPMgGyR2DBN+E8XSq3MpToUyu7ZIK0HUcUmsRapGK46wlfBuknWnk5AOsY3I2YsNmLAagPf1HMFNp+6S68FOD9mjhV+QxUM5THrohJDKNutWHpL8halvOqWo6yokk8fT58inSESI6ylST2EbDtGKRU49VitvtkJI8tOsg7OOJA1nFSzhQKaVIkT21OA23DV3Fdu51Yk6VICCREpzznS4pKPw3WDpXk34KOgD9+fZwxpWB4JNIIG1D1/xTinaSMvylJDy3YyjwDfUXH1pviFPhTGw/FkNuoOpbagofdxU2fHhMqekOBDadus4q+bJcwqahkssfxnrOFKKjckk8iodWcpUxDySS2rgcTfWMMPtvstvNKCkLSFJI5weMzFm6mZfQUvL32UQCiOg+N1q2DFbzlWa2paXHyzGOplolKbfKOtWLnb72FUp9NeD8GU4y4OdBtfr2jGW9JTbqm4tdQlCr2D6fIPzxzYadbdQhxpYUlQBBBuCD7+pVKPTIq5D6uAcCUjWpWwYqtWlVV9Tr6yE6kIHkpHJcl1cqS5TXjfc+O3f7xxedc6IoqTAgEKnqHCdYZB5ztVsGH5D0p5x+Q6px1ZKlKUbknico5zk0J5EWWtTtPWeFOstdKejaMR5TMxhuQw4lbTiQpKkm4UD7151thtbriwlCElSidQAxXaw7VZalXsyglLadg/M8mpstcKbHko1oWDbb0duGXEOtIcQbpUkKB2g8Tm3MSMv0xbySDJduhhB+FtPQMSJD0p5yRIcK3XFFSlK1kni9HealU+UijzFjvZ5X9iVHyHDzdSve5yqqm2kU5pViuynCNnMOUZVld80lgKsVNEtns4QPqPEKNgTjOdbVWq0+tC7xmCWmRzWTrV2njEqUhQUkkEG4Ixk6ue7dFjPuuXeau08Plp5+0cP6VrS22pSiAACSdgGKpMXPnSJK/PWSBsHMOzlGRX/EmsW8koWOs3B4jONTNNoNQkIUUr3ve27awpzxb4PCTxujGpKYqkinKV4klvdJ+e3+nMkjvakS1DWtIb7FcB+7BNyTyjI67S5CDzsqP1EcRpUkqRTqfFBtvr6l9iE2/nx2V5XeeYKS9/3CEdizuD+OEm4/RnVak0+OhJtd256gm38+U5JTeY+rYyofeniNKyjv8AR0c24f8AxTx1NJTUYKhrD7Z/iGEeSP0Z63Pe8Xc6hur9dxynI7JtNeOqyAO0m/EaVv1mj/Mf/FPHU7/mEL98j8cI8gfozq2pdOZWnmdseopJ5TlKIWKShZFi8tSz2eL/AC4jSsx/Y0qR8FbqD9IA8dQmFSK1S2UjypTQ7N0L4SLJ/RmOOJVIloSk+Ijdjb4nCcEWJB5PDjrlSWWGxdS1hI7TiHHRGjsso8htCUDqSLcRpDppl5ckLABXHUl8DYBwH7jx2juAZeYmXyk7iM2t07L23I/HA/QtIWkpULggjFXgqp8+RHINkrO5O0axyfJlLK3l1F1Pit3S3cecRr7BxMqM3IjusOpCkOoKVjakixGKzTXaTU5cB4HdNOEAnzk6we0cbo3o5g0hU91FnZhCh+7T5PvM6UjfWkTmE3W0LObSnmPZyanQHqjKajMjhUeE2uANpxAhNQYzTDabNtpsOk85PXxWkjLJmRk1mGjdPR0WdA85rb9HjMqUByv1Rtgg97N2W+vYjZ1qww02y2htCQlCEhKUjUAPeLQlxCkLAUlQsQdRBxmKiOUqWopSox1m6FHht0HkjDDsl1DLKCpajYAYoFFRSYw3dlSF8K1bPkji1JCgUkXBxnjJTlJecqVOZvCWbrQn9kT/AEniqVSplYmNQoTRW4s9iRzqUeYDGXaBFoFPbiMC6/KdctYrVt/Ie+qECNMjKjyE7oLHaOkYrVEkUl8hQKmVE7hY1HkUOFInPoYjtla1bMUDLzNKb3xyy5KvKXzDoTxrjaHEKQ4gKSoWIIuCDzYzTo5WlTk2ggEG6lxr6vmH+WHmXWHFtPNqQ4k2UlQIIOwg+/y/lCq19xKm2yzFv4z7g8X6I844oOXoFBiiPDb4TYuOny1kbTxEmOxKaVHebS4hXlA4rWTpEdSnqfdxu5JR5w6tuFtONKKXEFJBsQeOShSzZIvilZTnTShySCwyfhDxj1DFPpcSmtBuM0B8JR4VK6zyCr5apFaQROiJWsCwdT4qx1KGKloseG7XSp4UnmQ+LfxJxJyLmaMoj3OU4n4TakqwrLVfSbGjy/sV4ZyhmN/yKRI+kncf6rYhaM64+QZa2YyOk7tQ7E4o+jyiU0h2SgzHhzu+R2I/PCEIbASgAJAsAOLqFFp84HvphKlkCyhwK4OnZiXkcElUKV9Fz2hh/KdZataPuwfOSoEYXQqog2MJ49Taj/LHuNVPiEj7Jf5Y9xqp8QkfZL/LHuNVPiEj7Jf5Y9xqp8QkfZL/ACx7jVT4hI+yX+WPcaqfEJH2S/yx7jVT4hI+yX+WEUCquaoTw+chQ/EYYyjWHQSpgN9K1C33XOIuR0+VMlfRbH8ziFRKdTwksRkhY89XjK+/VyWwxYf5ef/EADgRAAIBAgMDCQUHBQAAAAAAAAECAwQRAAUgMUFhEhMhIjBAUXGREDJQU6EGFDNCYoGSUnKiwdH/2gAIAQIBAT8A+L37e/wE9zHfj3k90Gk90Gk9ztqPcbd3t3e3b2129qRySGyIScRZY56ZXtwGFoKZfyX8zj7rT/JX0w+X0zbFKngcTZdLHdozyx9cbOg9pbFtENJPNYqlh4nEOWxJYykufQYVFQWRQBw1VVGk4LKAJPHxwysjFWFiNUsscKGSVwqjecVOfgErSxX/AFNhs5r2P4oHkoxHndchHKZXHFf+YpM7gnISYc0/+J0KpYhVFycUtCkQDygM/huHZZjThl59R1l97iNMsqQxvLIbKoucV1dLWykkkRg9VdOUZmyOtLO10PQhO4+Hty6mCrz7jpPu+XZsoZSp2EEYkQxyOh/KSNGf1JAipVO3rNq2EHGW1P3mkikJ6w6reYxGpd0QbyBhVCqFGwC3aV4tUycbHRnLFq+UeAUfTX9nmJhqE3BwfUYoxeqi8+1ryDVPwA0ZwCMwm4hT9Nf2eB5qobcWUfTFM3Inib9Q7QkAEnYMSvzkrv4knRn8BEkVQB0Ecg+Y15RTmCij5Qsz9c/v7KWYTQo28dDefZ5hUBI+aU9Z9vAaamnSqheF9jD0OKmmlpZWilFiNh3Eacqy9quUSSLaFDc8T4YAt7KWpNPJfap94YR1kUOhuD2NTVJTr4vuGHdpHZ3NydVVSQVaciZfIjaMVOR1URJhtKvocNSVSmzU8gP9pxHQVkhASnf9xbFJkJuHq2Fv6F/2cIiRoqIoVQLADRBUSwG6Ho3g7DiLMYX6Huh9RgTwtslT1GOdi+YnqMc7F8xP5DHOxfMT+Qxz0XzE9Rh6ymTbKD5dOJsyY3WFbcThmZiWYkk7z8W//8QAOREAAgECAgYHBwMDBQAAAAAAAQIDAAQFERITICExkQYwQVFSYXEQFCJAQlOBMlChI4KSYnJzsbL/2gAIAQMBAT8A/YCyjiwFa2PxjnWtj8Y51rY/GOda2PxjnWtj8Y51rY/GOda2PxjnWtj8Y51rY/GOda2PxjnWtj8YoMp4EHq5LlV3LvNPNI/FuXW5kcDUdw6cd4pJFkGanbJABJqacvmq7l+RR2Rgy0jiRQw2rmXM6CncOPydq+T6B4HZmfQjJ7eA+UQ6LqfMbN229V/Pyg4j1GzcnOVvlIV0pFH52bgZSt8pbRaC6TcTs3YycHvHyQBJAFQ2+WTyfgbVymlHmOI+Rjt3fe3wio4kj4Df39RNGY38jw60AscgMzSWrHe5yFJEkfBd/f1UiLIpU1JG0ZyPVJE7/pWktRxc/gUqKgyVQOtZVcZMMxUlqw3pvHdRBU5EEbIBO4CktpG3t8IpLeNOzM+fsSN5DkikmosPY75Wy8hS2duv0Z+te7wfaXlT2Nu3BSvoalsJE3xnTH81vG49UVVtzAGjbRH6cq90TxGvdE8RoW0Q7M6Cqu5VA9kVrNLvC5DvNRWEa75CWPIUqqgyVQB5bVzarMCy7n7++mUoxVhkRtW9tPdypBbRNJI3BVFYf0FdlWTErnQP24uP5JqLojgUYyNqznvZ2q46GYLKDq0khPejk/8ArOsU6HX1irTWre8xDeQBk4/FHduPtALEKozJq3skjAaQaT/wOqv4NJdco3jj6bNtby3c8VtAulJIwVRWCYJb4PbKqqGnYDWSdpPcPLZ6V9HEmikxOxjAlQaUqL9Q7x5+2xgCrrmG8/p9OrIDAg8CKkTQd07iRsdBcPV3ucSkX9H9KP1O8naIBBBG410gsBh2K3MCDKNjrE/2tSLpuqDtIFKAqhRwA6y9GVw/mAdjohEEwK2I4u0jH/Lb6exgXljL2tEwP9pq0GdzF69bfHO4fyAGx0ScPgVpl9JkB/yO309cG6w9O0ROeZq3bQnib/UOsJyBJqV9ZI7952Ogl8DDdYezfEra1B5HcdvpTfC+xicoc44QIl/t4/z7LaUTRK3bwPr1d9PoJqlPxN/A2cOvpsNvIbyA/Eh3jvHaDWHYjbYnapdWzgg/qHap7js9JseTDLZreBwbuVSAB9AP1GiSSSeJ9ltcGB8/pPEUjq6hlOYPU3FykC97dgp3aRi7HMnaw3FbzCptdaSZeJDvVh5isO6aYdcqq3gNvJ25705ikxXDJAGS/gI/5FqfHMIt10pb+H0DBjyGdYr03XRaLCojnw1sg/6FTTSzyPNNIXkc5szHMnYhuJIDmh3doPCo7+F9z5oaE0R4SrzrWR/cXnWsj+4vOtZH9xeYrWx/cXmKe6gTjID6b6lxAnMQrl5mmYsSzEkn92//2Q==" + ], + "image", + ), + (["hello world"], "text"), + ], +) +def test_cohere_img_embeddings(input, input_type): + litellm.set_verbose = True + response = embedding( + model="cohere/embed-english-v3.0", + input=input, + ) + + if input_type == "image": + assert response.usage.prompt_tokens_details.image_tokens > 0 + else: + assert response.usage.prompt_tokens_details.text_tokens > 0 diff --git a/tests/local_testing/test_get_llm_provider.py b/tests/local_testing/test_get_llm_provider.py index e723738054..f7126cec07 100644 --- a/tests/local_testing/test_get_llm_provider.py +++ b/tests/local_testing/test_get_llm_provider.py @@ -160,3 +160,12 @@ def test_get_llm_provider_jina_ai(): assert custom_llm_provider == "openai_like" assert api_base == "https://api.jina.ai/v1" assert model == "jina-embeddings-v3" + + +def test_get_llm_provider_hosted_vllm(): + model, custom_llm_provider, dynamic_api_key, api_base = litellm.get_llm_provider( + model="hosted_vllm/llama-3.1-70b-instruct", + ) + assert custom_llm_provider == "hosted_vllm" + assert model == "llama-3.1-70b-instruct" + assert dynamic_api_key == "" diff --git a/tests/local_testing/test_prompt_factory.py b/tests/local_testing/test_prompt_factory.py index 74e7cefa55..7b4e295ce1 100644 --- a/tests/local_testing/test_prompt_factory.py +++ b/tests/local_testing/test_prompt_factory.py @@ -675,3 +675,15 @@ def test_alternating_roles_e2e(): "stream": False, } ) + + +def test_just_system_message(): + from litellm.llms.prompt_templates.factory import _bedrock_converse_messages_pt + + with pytest.raises(litellm.BadRequestError) as e: + _bedrock_converse_messages_pt( + messages=[], + model="anthropic.claude-3-sonnet-20240229-v1:0", + llm_provider="bedrock", + ) + assert "bedrock requires at least one non-system message" in str(e.value) diff --git a/tests/local_testing/test_proxy_server.py b/tests/local_testing/test_proxy_server.py index 8032435577..51ec085ba3 100644 --- a/tests/local_testing/test_proxy_server.py +++ b/tests/local_testing/test_proxy_server.py @@ -225,12 +225,20 @@ def test_add_headers_to_request(litellm_key_header_name): "litellm_key_header_name", ["x-litellm-key", None], ) +@pytest.mark.parametrize( + "forward_headers", + [True, False], +) @mock_patch_acompletion() def test_chat_completion_forward_headers( - mock_acompletion, client_no_auth, litellm_key_header_name + mock_acompletion, client_no_auth, litellm_key_header_name, forward_headers ): global headers try: + if forward_headers: + gs = getattr(litellm.proxy.proxy_server, "general_settings") + gs["forward_client_headers_to_llm_api"] = True + setattr(litellm.proxy.proxy_server, "general_settings", gs) if litellm_key_header_name is not None: gs = getattr(litellm.proxy.proxy_server, "general_settings") gs["litellm_key_header_name"] = litellm_key_header_name @@ -260,23 +268,14 @@ def test_chat_completion_forward_headers( response = client_no_auth.post( "/v1/chat/completions", json=test_data, headers=received_headers ) - mock_acompletion.assert_called_once_with( - model="gpt-3.5-turbo", - messages=[ - {"role": "user", "content": "hi"}, - ], - max_tokens=10, - litellm_call_id=mock.ANY, - litellm_logging_obj=mock.ANY, - request_timeout=mock.ANY, - specific_deployment=True, - metadata=mock.ANY, - proxy_server_request=mock.ANY, - headers={ + if not forward_headers: + assert "headers" not in mock_acompletion.call_args.kwargs + else: + assert mock_acompletion.call_args.kwargs["headers"] == { "x-custom-header": "Custom-Value", "x-another-header": "Another-Value", - }, - ) + } + print(f"response - {response.text}") assert response.status_code == 200 result = response.json() diff --git a/tests/local_testing/test_utils.py b/tests/local_testing/test_utils.py index 9c26da614a..3558f88bc2 100644 --- a/tests/local_testing/test_utils.py +++ b/tests/local_testing/test_utils.py @@ -331,6 +331,13 @@ def test_validate_environment_api_key(): ), f"Missing keys={response_obj['missing_keys']}" +def test_validate_environment_api_base_dynamic(): + for provider in ["ollama", "ollama_chat"]: + kv = validate_environment(provider + "/mistral", api_base="https://example.com") + assert kv["keys_in_environment"] + assert kv["missing_keys"] == [] + + @mock.patch.dict(os.environ, {"OLLAMA_API_BASE": "foo"}, clear=True) def test_validate_environment_ollama(): for provider in ["ollama", "ollama_chat"]: From fb9fb3467d7f076bc9b623fee255a376db7ba255 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 26 Oct 2024 11:41:37 +0400 Subject: [PATCH 59/62] (UI) Delete Internal Users on Admin UI (#6442) * add /user/delete call * ui show modal asking if you want to delete user * fix delete user modal --- .../src/components/networking.tsx | 33 +++++++ .../src/components/view_users.tsx | 87 +++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index fb959d380f..874c522d3a 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -521,6 +521,39 @@ export const keyDeleteCall = async (accessToken: String, user_key: String) => { } }; +export const userDeleteCall = async (accessToken: string, userIds: string[]) => { + try { + const url = proxyBaseUrl ? `${proxyBaseUrl}/user/delete` : `/user/delete`; + console.log("in userDeleteCall:", userIds); + + const response = await fetch(url, { + method: "POST", + headers: { + [globalLitellmHeaderName]: `Bearer ${accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + user_ids: userIds, + }), + }); + + if (!response.ok) { + const errorData = await response.text(); + handleError(errorData); + throw new Error("Network response was not ok"); + } + + const data = await response.json(); + console.log(data); + //message.success("User(s) Deleted"); + return data; + } catch (error) { + console.error("Failed to delete user(s):", error); + throw error; + } +}; + + export const teamDeleteCall = async (accessToken: String, teamID: String) => { try { const url = proxyBaseUrl ? `${proxyBaseUrl}/team/delete` : `/team/delete`; diff --git a/ui/litellm-dashboard/src/components/view_users.tsx b/ui/litellm-dashboard/src/components/view_users.tsx index e3be74706d..58374a0c84 100644 --- a/ui/litellm-dashboard/src/components/view_users.tsx +++ b/ui/litellm-dashboard/src/components/view_users.tsx @@ -26,6 +26,7 @@ import { } from "@tremor/react"; import { message } from "antd"; +import { Modal } from "antd"; import { userInfoCall, @@ -43,6 +44,8 @@ import { TrashIcon, } from "@heroicons/react/outline"; +import { userDeleteCall } from "./networking"; + interface ViewUserDashboardProps { accessToken: string | null; token: string | null; @@ -84,11 +87,42 @@ const ViewUserDashboard: React.FC = ({ const [selectedItem, setSelectedItem] = useState(null); const [editModalVisible, setEditModalVisible] = useState(false); const [selectedUser, setSelectedUser] = useState(null); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [userToDelete, setUserToDelete] = useState(null); const [possibleUIRoles, setPossibleUIRoles] = useState< Record> >({}); const defaultPageSize = 25; + const handleDelete = (userId: string) => { + setUserToDelete(userId); + setIsDeleteModalOpen(true); + }; + + const confirmDelete = async () => { + if (userToDelete && accessToken) { + try { + await userDeleteCall(accessToken, [userToDelete]); + message.success("User deleted successfully"); + // Update the user list after deletion + if (userData) { + const updatedUserData = userData.filter(user => user.user_id !== userToDelete); + setUserData(updatedUserData); + } + } catch (error) { + console.error("Error deleting user:", error); + message.error("Failed to delete user"); + } + } + setIsDeleteModalOpen(false); + setUserToDelete(null); + }; + + const cancelDelete = () => { + setIsDeleteModalOpen(false); + setUserToDelete(null); + }; + const handleEditCancel = async () => { setSelectedUser(null); setEditModalVisible(false); @@ -272,6 +306,12 @@ const ViewUserDashboard: React.FC = ({ > View Keys + handleDelete(user.user_id)} + > + Delete +
))} @@ -293,6 +333,53 @@ const ViewUserDashboard: React.FC = ({ user={selectedUser} onSubmit={handleEditSubmit} /> + {isDeleteModalOpen && ( +
+
+ + + {/* Modal Panel */} + + + {/* Confirmation Modal Content */} +
+
+
+
+

+ Delete User +

+
+

+ Are you sure you want to delete this user? +

+

+ User ID: {userToDelete} +

+
+
+
+
+
+ + +
+
+
+
+ )} {renderPagination()} From 151991c66dc8174c167de7666aacd2267dd12f87 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 28 Oct 2024 18:08:05 +0400 Subject: [PATCH 60/62] (testing) increase prometheus.py test coverage to 90% (#6466) * testing for failure events prometheus * set set_llm_deployment_failure_metrics * test_async_post_call_failure_hook * unit testing for all prometheus functions * fix linting --- litellm/integrations/prometheus.py | 47 +- .../test_prometheus_unit_tests.py | 431 ++++++++++++++++++ 2 files changed, 461 insertions(+), 17 deletions(-) diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index 54563ab61e..01c08ef04e 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -397,7 +397,10 @@ class PrometheusLogger(CustomLogger): # input, output, total token metrics self._increment_token_metrics( - standard_logging_payload=standard_logging_payload, + # why type ignore below? + # 1. We just checked if isinstance(standard_logging_payload, dict). Pyright complains. + # 2. Pyright does not allow us to run isinstance(standard_logging_payload, StandardLoggingPayload) <- this would be ideal + standard_logging_payload=standard_logging_payload, # type: ignore end_user_id=end_user_id, user_api_key=user_api_key, user_api_key_alias=user_api_key_alias, @@ -432,7 +435,10 @@ class PrometheusLogger(CustomLogger): user_api_key_alias=user_api_key_alias, user_api_team=user_api_team, user_api_team_alias=user_api_team_alias, - standard_logging_payload=standard_logging_payload, + # why type ignore below? + # 1. We just checked if isinstance(standard_logging_payload, dict). Pyright complains. + # 2. Pyright does not allow us to run isinstance(standard_logging_payload, StandardLoggingPayload) <- this would be ideal + standard_logging_payload=standard_logging_payload, # type: ignore ) # set x-ratelimit headers @@ -757,24 +763,31 @@ class PrometheusLogger(CustomLogger): pass def set_llm_deployment_failure_metrics(self, request_kwargs: dict): + """ + Sets Failure metrics when an LLM API call fails + + - mark the deployment as partial outage + - increment deployment failure responses metric + - increment deployment total requests metric + + Args: + request_kwargs: dict + + """ try: verbose_logger.debug("setting remaining tokens requests metric") standard_logging_payload: StandardLoggingPayload = request_kwargs.get( "standard_logging_object", {} ) - _response_headers = request_kwargs.get("response_headers") _litellm_params = request_kwargs.get("litellm_params", {}) or {} - _metadata = _litellm_params.get("metadata", {}) litellm_model_name = request_kwargs.get("model", None) - api_base = _metadata.get("api_base", None) - model_group = _metadata.get("model_group", None) - if api_base is None: - api_base = _litellm_params.get("api_base", None) - llm_provider = _litellm_params.get("custom_llm_provider", None) - _model_info = _metadata.get("model_info") or {} - model_id = _model_info.get("id", None) + model_group = standard_logging_payload.get("model_group", None) + api_base = standard_logging_payload.get("api_base", None) + model_id = standard_logging_payload.get("model_id", None) exception: Exception = request_kwargs.get("exception", None) + llm_provider = _litellm_params.get("custom_llm_provider", None) + """ log these labels ["litellm_model_name", "model_id", "api_base", "api_provider"] @@ -1061,8 +1074,8 @@ class PrometheusLogger(CustomLogger): self, state: int, litellm_model_name: str, - model_id: str, - api_base: str, + model_id: Optional[str], + api_base: Optional[str], api_provider: str, ): self.litellm_deployment_state.labels( @@ -1083,8 +1096,8 @@ class PrometheusLogger(CustomLogger): def set_deployment_partial_outage( self, litellm_model_name: str, - model_id: str, - api_base: str, + model_id: Optional[str], + api_base: Optional[str], api_provider: str, ): self.set_litellm_deployment_state( @@ -1094,8 +1107,8 @@ class PrometheusLogger(CustomLogger): def set_deployment_complete_outage( self, litellm_model_name: str, - model_id: str, - api_base: str, + model_id: Optional[str], + api_base: Optional[str], api_provider: str, ): self.set_litellm_deployment_state( diff --git a/tests/logging_callback_tests/test_prometheus_unit_tests.py b/tests/logging_callback_tests/test_prometheus_unit_tests.py index 0355692737..a2c49b35a1 100644 --- a/tests/logging_callback_tests/test_prometheus_unit_tests.py +++ b/tests/logging_callback_tests/test_prometheus_unit_tests.py @@ -26,6 +26,7 @@ import pytest from unittest.mock import MagicMock, patch from datetime import datetime, timedelta from litellm.integrations.prometheus import PrometheusLogger +from litellm.proxy._types import UserAPIKeyAuth verbose_logger.setLevel(logging.DEBUG) @@ -67,6 +68,7 @@ def create_standard_logging_payload() -> StandardLoggingPayload: user_api_key_team_id="test_team", user_api_key_user_id="test_user", user_api_key_team_alias="test_team_alias", + user_api_key_org_id=None, spend_logs_metadata=None, requester_ip_address="127.0.0.1", requester_metadata=None, @@ -342,3 +344,432 @@ def test_increment_top_level_request_and_spend_metrics(prometheus_logger): "user1", "key1", "alias1", "gpt-3.5-turbo", "team1", "team_alias1", "user1" ) prometheus_logger.litellm_spend_metric.labels().inc.assert_called_once_with(0.1) + + +@pytest.mark.asyncio +async def test_async_log_failure_event(prometheus_logger): + # NOTE: almost all params for this metric are read from standard logging payload + standard_logging_object = create_standard_logging_payload() + kwargs = { + "model": "gpt-3.5-turbo", + "litellm_params": { + "custom_llm_provider": "openai", + }, + "start_time": datetime.now(), + "completion_start_time": datetime.now(), + "api_call_start_time": datetime.now(), + "end_time": datetime.now() + timedelta(seconds=1), + "standard_logging_object": standard_logging_object, + "exception": Exception("Test error"), + } + response_obj = MagicMock() + + # Mock the metrics + prometheus_logger.litellm_llm_api_failed_requests_metric = MagicMock() + prometheus_logger.litellm_deployment_failure_responses = MagicMock() + prometheus_logger.litellm_deployment_total_requests = MagicMock() + prometheus_logger.set_deployment_partial_outage = MagicMock() + + await prometheus_logger.async_log_failure_event( + kwargs, response_obj, kwargs["start_time"], kwargs["end_time"] + ) + + # litellm_llm_api_failed_requests_metric incremented + """ + Expected metrics + end_user_id, + user_api_key, + user_api_key_alias, + model, + user_api_team, + user_api_team_alias, + user_id, + """ + prometheus_logger.litellm_llm_api_failed_requests_metric.labels.assert_called_once_with( + None, + "test_hash", + "test_alias", + "gpt-3.5-turbo", + "test_team", + "test_team_alias", + "test_user", + ) + prometheus_logger.litellm_llm_api_failed_requests_metric.labels().inc.assert_called_once() + + # deployment should be marked in partial outage + prometheus_logger.set_deployment_partial_outage.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + ) + + # deployment failure responses incremented + prometheus_logger.litellm_deployment_failure_responses.labels.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + exception_status="None", + exception_class="Exception", + requested_model="openai-gpt", # passed in standard logging payload + hashed_api_key="test_hash", + api_key_alias="test_alias", + team="test_team", + team_alias="test_team_alias", + ) + prometheus_logger.litellm_deployment_failure_responses.labels().inc.assert_called_once() + + # deployment total requests incremented + prometheus_logger.litellm_deployment_total_requests.labels.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + requested_model="openai-gpt", # passed in standard logging payload + hashed_api_key="test_hash", + api_key_alias="test_alias", + team="test_team", + team_alias="test_team_alias", + ) + prometheus_logger.litellm_deployment_total_requests.labels().inc.assert_called_once() + + +@pytest.mark.asyncio +async def test_async_post_call_failure_hook(prometheus_logger): + """ + Test for the async_post_call_failure_hook method + + it should increment the litellm_proxy_failed_requests_metric and litellm_proxy_total_requests_metric + """ + # Mock the prometheus metrics + prometheus_logger.litellm_proxy_failed_requests_metric = MagicMock() + prometheus_logger.litellm_proxy_total_requests_metric = MagicMock() + + # Create test data + request_data = {"model": "gpt-3.5-turbo"} + + original_exception = litellm.RateLimitError( + message="Test error", llm_provider="openai", model="gpt-3.5-turbo" + ) + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + key_alias="test_alias", + team_id="test_team", + team_alias="test_team_alias", + user_id="test_user", + end_user_id="test_end_user", + ) + + # Call the function + await prometheus_logger.async_post_call_failure_hook( + request_data=request_data, + original_exception=original_exception, + user_api_key_dict=user_api_key_dict, + ) + + # Assert failed requests metric was incremented with correct labels + prometheus_logger.litellm_proxy_failed_requests_metric.labels.assert_called_once_with( + end_user="test_end_user", + hashed_api_key="test_key", + api_key_alias="test_alias", + requested_model="gpt-3.5-turbo", + team="test_team", + team_alias="test_team_alias", + user="test_user", + exception_status=429, + exception_class="RateLimitError", + ) + prometheus_logger.litellm_proxy_failed_requests_metric.labels().inc.assert_called_once() + + # Assert total requests metric was incremented with correct labels + prometheus_logger.litellm_proxy_total_requests_metric.labels.assert_called_once_with( + "test_end_user", + "test_key", + "test_alias", + "gpt-3.5-turbo", + "test_team", + "test_team_alias", + "test_user", + ) + prometheus_logger.litellm_proxy_total_requests_metric.labels().inc.assert_called_once() + + +@pytest.mark.asyncio +async def test_async_post_call_success_hook(prometheus_logger): + """ + Test for the async_post_call_success_hook method + + it should increment the litellm_proxy_total_requests_metric + """ + # Mock the prometheus metric + prometheus_logger.litellm_proxy_total_requests_metric = MagicMock() + + # Create test data + data = {"model": "gpt-3.5-turbo"} + + user_api_key_dict = UserAPIKeyAuth( + api_key="test_key", + key_alias="test_alias", + team_id="test_team", + team_alias="test_team_alias", + user_id="test_user", + end_user_id="test_end_user", + ) + + response = {"choices": [{"message": {"content": "test response"}}]} + + # Call the function + await prometheus_logger.async_post_call_success_hook( + data=data, user_api_key_dict=user_api_key_dict, response=response + ) + + # Assert total requests metric was incremented with correct labels + prometheus_logger.litellm_proxy_total_requests_metric.labels.assert_called_once_with( + "test_end_user", + "test_key", + "test_alias", + "gpt-3.5-turbo", + "test_team", + "test_team_alias", + "test_user", + ) + prometheus_logger.litellm_proxy_total_requests_metric.labels().inc.assert_called_once() + + +def test_set_llm_deployment_success_metrics(prometheus_logger): + # Mock all the metrics used in the method + prometheus_logger.litellm_remaining_requests_metric = MagicMock() + prometheus_logger.litellm_remaining_tokens_metric = MagicMock() + prometheus_logger.litellm_deployment_success_responses = MagicMock() + prometheus_logger.litellm_deployment_total_requests = MagicMock() + prometheus_logger.litellm_deployment_latency_per_output_token = MagicMock() + prometheus_logger.set_deployment_healthy = MagicMock() + + standard_logging_payload = create_standard_logging_payload() + + # Create test data + request_kwargs = { + "model": "gpt-3.5-turbo", + "response_headers": { + "x-ratelimit-remaining-requests": 123, + "x-ratelimit-remaining-tokens": 4321, + }, + "litellm_params": { + "custom_llm_provider": "openai", + "metadata": {"model_info": {"id": "model-123"}}, + }, + "standard_logging_object": standard_logging_payload, + } + + start_time = datetime.now() + end_time = start_time + timedelta(seconds=1) + output_tokens = 10 + + # Call the function + prometheus_logger.set_llm_deployment_success_metrics( + request_kwargs=request_kwargs, + start_time=start_time, + end_time=end_time, + output_tokens=output_tokens, + ) + + # Verify remaining requests metric + prometheus_logger.litellm_remaining_requests_metric.labels.assert_called_once_with( + "openai-gpt", # model_group / requested model from create_standard_logging_payload() + "openai", # llm provider + "https://api.openai.com", # api base + "gpt-3.5-turbo", # actual model used - litellm model name + standard_logging_payload["metadata"]["user_api_key_hash"], + standard_logging_payload["metadata"]["user_api_key_alias"], + ) + prometheus_logger.litellm_remaining_requests_metric.labels().set.assert_called_once_with( + 123 + ) + + # Verify remaining tokens metric + prometheus_logger.litellm_remaining_tokens_metric.labels.assert_called_once_with( + "openai-gpt", # model_group / requested model from create_standard_logging_payload() + "openai", # llm provider + "https://api.openai.com", # api base + "gpt-3.5-turbo", # actual model used - litellm model name + standard_logging_payload["metadata"]["user_api_key_hash"], + standard_logging_payload["metadata"]["user_api_key_alias"], + ) + prometheus_logger.litellm_remaining_tokens_metric.labels().set.assert_called_once_with( + 4321 + ) + + # Verify deployment healthy state + prometheus_logger.set_deployment_healthy.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + ) + + # Verify success responses metric + prometheus_logger.litellm_deployment_success_responses.labels.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + requested_model="openai-gpt", # requested model from create_standard_logging_payload() + hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"], + api_key_alias=standard_logging_payload["metadata"]["user_api_key_alias"], + team=standard_logging_payload["metadata"]["user_api_key_team_id"], + team_alias=standard_logging_payload["metadata"]["user_api_key_team_alias"], + ) + prometheus_logger.litellm_deployment_success_responses.labels().inc.assert_called_once() + + # Verify total requests metric + prometheus_logger.litellm_deployment_total_requests.labels.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + requested_model="openai-gpt", # requested model from create_standard_logging_payload() + hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"], + api_key_alias=standard_logging_payload["metadata"]["user_api_key_alias"], + team=standard_logging_payload["metadata"]["user_api_key_team_id"], + team_alias=standard_logging_payload["metadata"]["user_api_key_team_alias"], + ) + prometheus_logger.litellm_deployment_total_requests.labels().inc.assert_called_once() + + # Verify latency per output token metric + prometheus_logger.litellm_deployment_latency_per_output_token.labels.assert_called_once_with( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + hashed_api_key=standard_logging_payload["metadata"]["user_api_key_hash"], + api_key_alias=standard_logging_payload["metadata"]["user_api_key_alias"], + team=standard_logging_payload["metadata"]["user_api_key_team_id"], + team_alias=standard_logging_payload["metadata"]["user_api_key_team_alias"], + ) + # Calculate expected latency per token (1 second / 10 tokens = 0.1 seconds per token) + expected_latency_per_token = 0.1 + prometheus_logger.litellm_deployment_latency_per_output_token.labels().observe.assert_called_once_with( + expected_latency_per_token + ) + + +@pytest.mark.asyncio +async def test_log_success_fallback_event(prometheus_logger): + prometheus_logger.litellm_deployment_successful_fallbacks = MagicMock() + + original_model_group = "gpt-3.5-turbo" + kwargs = { + "model": "gpt-4", + "metadata": { + "user_api_key_hash": "test_hash", + "user_api_key_alias": "test_alias", + "user_api_key_team_id": "test_team", + "user_api_key_team_alias": "test_team_alias", + }, + } + original_exception = litellm.RateLimitError( + message="Test error", llm_provider="openai", model="gpt-3.5-turbo" + ) + + await prometheus_logger.log_success_fallback_event( + original_model_group=original_model_group, + kwargs=kwargs, + original_exception=original_exception, + ) + + prometheus_logger.litellm_deployment_successful_fallbacks.labels.assert_called_once_with( + requested_model=original_model_group, + fallback_model="gpt-4", + hashed_api_key="test_hash", + api_key_alias="test_alias", + team="test_team", + team_alias="test_team_alias", + exception_status="429", + exception_class="RateLimitError", + ) + prometheus_logger.litellm_deployment_successful_fallbacks.labels().inc.assert_called_once() + + +@pytest.mark.asyncio +async def test_log_failure_fallback_event(prometheus_logger): + prometheus_logger.litellm_deployment_failed_fallbacks = MagicMock() + + original_model_group = "gpt-3.5-turbo" + kwargs = { + "model": "gpt-4", + "metadata": { + "user_api_key_hash": "test_hash", + "user_api_key_alias": "test_alias", + "user_api_key_team_id": "test_team", + "user_api_key_team_alias": "test_team_alias", + }, + } + original_exception = litellm.RateLimitError( + message="Test error", llm_provider="openai", model="gpt-3.5-turbo" + ) + + await prometheus_logger.log_failure_fallback_event( + original_model_group=original_model_group, + kwargs=kwargs, + original_exception=original_exception, + ) + + prometheus_logger.litellm_deployment_failed_fallbacks.labels.assert_called_once_with( + requested_model=original_model_group, + fallback_model="gpt-4", + hashed_api_key="test_hash", + api_key_alias="test_alias", + team="test_team", + team_alias="test_team_alias", + exception_status="429", + exception_class="RateLimitError", + ) + prometheus_logger.litellm_deployment_failed_fallbacks.labels().inc.assert_called_once() + + +def test_deployment_state_management(prometheus_logger): + prometheus_logger.litellm_deployment_state = MagicMock() + + test_params = { + "litellm_model_name": "gpt-3.5-turbo", + "model_id": "model-123", + "api_base": "https://api.openai.com", + "api_provider": "openai", + } + + # Test set_deployment_healthy (state=0) + prometheus_logger.set_deployment_healthy(**test_params) + prometheus_logger.litellm_deployment_state.labels.assert_called_with( + test_params["litellm_model_name"], + test_params["model_id"], + test_params["api_base"], + test_params["api_provider"], + ) + prometheus_logger.litellm_deployment_state.labels().set.assert_called_with(0) + + # Test set_deployment_partial_outage (state=1) + prometheus_logger.set_deployment_partial_outage(**test_params) + prometheus_logger.litellm_deployment_state.labels().set.assert_called_with(1) + + # Test set_deployment_complete_outage (state=2) + prometheus_logger.set_deployment_complete_outage(**test_params) + prometheus_logger.litellm_deployment_state.labels().set.assert_called_with(2) + + +def test_increment_deployment_cooled_down(prometheus_logger): + prometheus_logger.litellm_deployment_cooled_down = MagicMock() + + prometheus_logger.increment_deployment_cooled_down( + litellm_model_name="gpt-3.5-turbo", + model_id="model-123", + api_base="https://api.openai.com", + api_provider="openai", + exception_status="429", + ) + + prometheus_logger.litellm_deployment_cooled_down.labels.assert_called_once_with( + "gpt-3.5-turbo", "model-123", "https://api.openai.com", "openai", "429" + ) + prometheus_logger.litellm_deployment_cooled_down.labels().inc.assert_called_once() From 030ece8c3fc0216dc39fd915e551e04ecdc1e2b0 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 28 Oct 2024 22:01:32 +0530 Subject: [PATCH 61/62] (Feat) New Logging integration - add Datadog LLM Observability support (#6449) * add type for dd llm obs request ob * working dd llm obs * datadog use well defined type * clean up * unit test test_create_llm_obs_payload * fix linting * add datadog_llm_observability * add datadog_llm_observability * docs DD LLM obs * run testing again * document DD_ENV * test_create_llm_obs_payload --- docs/my-website/docs/proxy/configs.md | 1 + docs/my-website/docs/proxy/logging.md | 22 +++ litellm/__init__.py | 1 + .../integrations/datadog/datadog_llm_obs.py | 169 ++++++++++++++++++ litellm/litellm_core_utils/litellm_logging.py | 9 + litellm/types/integrations/datadog_llm_obs.py | 52 ++++++ .../test_datadog_llm_obs.py | 141 +++++++++++++++ .../test_unit_tests_init_callbacks.py | 2 + 8 files changed, 397 insertions(+) create mode 100644 litellm/integrations/datadog/datadog_llm_obs.py create mode 100644 litellm/types/integrations/datadog_llm_obs.py create mode 100644 tests/logging_callback_tests/test_datadog_llm_obs.py diff --git a/docs/my-website/docs/proxy/configs.md b/docs/my-website/docs/proxy/configs.md index ee9a9096fa..28b0b67e33 100644 --- a/docs/my-website/docs/proxy/configs.md +++ b/docs/my-website/docs/proxy/configs.md @@ -918,6 +918,7 @@ router_settings: | DD_API_KEY | API key for Datadog integration | DD_SITE | Site URL for Datadog (e.g., datadoghq.com) | DD_SOURCE | Source identifier for Datadog logs +| DD_ENV | Environment identifier for Datadog logs. Only supported for `datadog_llm_observability` callback | DEBUG_OTEL | Enable debug mode for OpenTelemetry | DIRECT_URL | Direct URL for service endpoint | DISABLE_ADMIN_UI | Toggle to disable the admin UI diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 72c2e3773b..94faa7734a 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -1468,6 +1468,13 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ ## Logging Proxy Input/Output - DataDog +LiteLLM Supports logging to the following Datdog Integrations: +- `datadog` [Datadog Logs](https://docs.datadoghq.com/logs/) +- `datadog_llm_observability` [Datadog LLM Observability](https://www.datadoghq.com/product/llm-observability/) + + + + We will use the `--config` to set `litellm.success_callback = ["datadog"]` this will log all successfull LLM calls to DataDog **Step 1**: Create a `config.yaml` file and set `litellm_settings`: `success_callback` @@ -1482,6 +1489,21 @@ litellm_settings: service_callback: ["datadog"] # logs redis, postgres failures on datadog ``` + + + +```yaml +model_list: + - model_name: gpt-3.5-turbo + litellm_params: + model: gpt-3.5-turbo +litellm_settings: + callbacks: ["datadog_llm_observability"] # logs llm success logs on datadog +``` + + + + **Step 2**: Set Required env variables for datadog ```shell diff --git a/litellm/__init__.py b/litellm/__init__.py index b1033e7a49..a42a8f90d8 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -49,6 +49,7 @@ _custom_logger_compatible_callbacks_literal = Literal[ "langsmith", "prometheus", "datadog", + "datadog_llm_observability", "galileo", "braintrust", "arize", diff --git a/litellm/integrations/datadog/datadog_llm_obs.py b/litellm/integrations/datadog/datadog_llm_obs.py new file mode 100644 index 0000000000..9666c45814 --- /dev/null +++ b/litellm/integrations/datadog/datadog_llm_obs.py @@ -0,0 +1,169 @@ +""" +Implements logging integration with Datadog's LLM Observability Service + + +API Reference: https://docs.datadoghq.com/llm_observability/setup/api/?tab=example#api-standards + +""" + +import asyncio +import os +import traceback +import uuid +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from httpx import Response + +import litellm +from litellm._logging import verbose_logger +from litellm.integrations.custom_batch_logger import CustomBatchLogger +from litellm.llms.custom_httpx.http_handler import ( + get_async_httpx_client, + httpxSpecialProvider, +) +from litellm.types.integrations.datadog_llm_obs import * +from litellm.types.utils import StandardLoggingPayload + + +class DataDogLLMObsLogger(CustomBatchLogger): + def __init__(self, **kwargs): + try: + verbose_logger.debug("DataDogLLMObs: Initializing logger") + if os.getenv("DD_API_KEY", None) is None: + raise Exception("DD_API_KEY is not set, set 'DD_API_KEY=<>'") + if os.getenv("DD_SITE", None) is None: + raise Exception( + "DD_SITE is not set, set 'DD_SITE=<>', example sit = `us5.datadoghq.com`" + ) + + self.async_client = get_async_httpx_client( + llm_provider=httpxSpecialProvider.LoggingCallback + ) + self.DD_API_KEY = os.getenv("DD_API_KEY") + self.DD_SITE = os.getenv("DD_SITE") + self.intake_url = ( + f"https://api.{self.DD_SITE}/api/intake/llm-obs/v1/trace/spans" + ) + + # testing base url + dd_base_url = os.getenv("DD_BASE_URL") + if dd_base_url: + self.intake_url = f"{dd_base_url}/api/intake/llm-obs/v1/trace/spans" + + asyncio.create_task(self.periodic_flush()) + self.flush_lock = asyncio.Lock() + self.log_queue: List[LLMObsPayload] = [] + super().__init__(**kwargs, flush_lock=self.flush_lock) + except Exception as e: + verbose_logger.exception(f"DataDogLLMObs: Error initializing - {str(e)}") + raise e + + async def async_log_success_event(self, kwargs, response_obj, start_time, end_time): + try: + verbose_logger.debug( + f"DataDogLLMObs: Logging success event for model {kwargs.get('model', 'unknown')}" + ) + payload = self.create_llm_obs_payload( + kwargs, response_obj, start_time, end_time + ) + verbose_logger.debug(f"DataDogLLMObs: Payload: {payload}") + self.log_queue.append(payload) + + if len(self.log_queue) >= self.batch_size: + await self.async_send_batch() + except Exception as e: + verbose_logger.exception( + f"DataDogLLMObs: Error logging success event - {str(e)}" + ) + + async def async_send_batch(self): + try: + if not self.log_queue: + return + + verbose_logger.debug( + f"DataDogLLMObs: Flushing {len(self.log_queue)} events" + ) + + # Prepare the payload + payload = { + "data": DDIntakePayload( + type="span", + attributes=DDSpanAttributes( + ml_app="litellm", + tags=[ + "service:litellm", + f"env:{os.getenv('DD_ENV', 'production')}", + ], + spans=self.log_queue, + ), + ), + } + + response = await self.async_client.post( + url=self.intake_url, + json=payload, + headers={ + "DD-API-KEY": self.DD_API_KEY, + "Content-Type": "application/json", + }, + ) + + response.raise_for_status() + if response.status_code != 202: + raise Exception( + f"DataDogLLMObs: Unexpected response - status_code: {response.status_code}, text: {response.text}" + ) + + verbose_logger.debug( + f"DataDogLLMObs: Successfully sent batch - status_code: {response.status_code}" + ) + self.log_queue.clear() + except Exception as e: + verbose_logger.exception(f"DataDogLLMObs: Error sending batch - {str(e)}") + + def create_llm_obs_payload( + self, kwargs: Dict, response_obj: Any, start_time: datetime, end_time: datetime + ) -> LLMObsPayload: + standard_logging_payload: Optional[StandardLoggingPayload] = kwargs.get( + "standard_logging_object" + ) + if standard_logging_payload is None: + raise Exception("DataDogLLMObs: standard_logging_object is not set") + + messages = standard_logging_payload["messages"] + metadata = kwargs.get("litellm_params", {}).get("metadata", {}) + + input_meta = InputMeta(messages=messages) # type: ignore + output_meta = OutputMeta(messages=self._get_response_messages(response_obj)) + + meta = Meta(kind="llm", input=input_meta, output=output_meta) + + # Calculate metrics (you may need to adjust these based on available data) + metrics = LLMMetrics( + input_tokens=float(standard_logging_payload.get("prompt_tokens", 0)), + output_tokens=float(standard_logging_payload.get("completion_tokens", 0)), + total_tokens=float(standard_logging_payload.get("total_tokens", 0)), + ) + + return LLMObsPayload( + parent_id=metadata.get("parent_id", "undefined"), + trace_id=metadata.get("trace_id", str(uuid.uuid4())), + span_id=metadata.get("span_id", str(uuid.uuid4())), + name=metadata.get("name", "litellm_llm_call"), + meta=meta, + start_ns=int(start_time.timestamp() * 1e9), + duration=int((end_time - start_time).total_seconds() * 1e9), + metrics=metrics, + ) + + def _get_response_messages(self, response_obj: Any) -> List[Any]: + """ + Get the messages from the response object + + for now this handles logging /chat/completions responses + """ + if isinstance(response_obj, litellm.ModelResponse): + return [response_obj["choices"][0]["message"].json()] + return [] diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 206cb235e7..7f403e422c 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -64,6 +64,7 @@ from ..integrations.arize_ai import ArizeLogger from ..integrations.athina import AthinaLogger from ..integrations.braintrust_logging import BraintrustLogger from ..integrations.datadog.datadog import DataDogLogger +from ..integrations.datadog.datadog_llm_obs import DataDogLLMObsLogger from ..integrations.dynamodb import DyanmoDBLogger from ..integrations.galileo import GalileoObserve from ..integrations.gcs_bucket.gcs_bucket import GCSBucketLogger @@ -2205,6 +2206,10 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 _datadog_logger = DataDogLogger() _in_memory_loggers.append(_datadog_logger) return _datadog_logger # type: ignore + elif logging_integration == "datadog_llm_observability": + _datadog_llm_obs_logger = DataDogLLMObsLogger() + _in_memory_loggers.append(_datadog_llm_obs_logger) + return _datadog_llm_obs_logger # type: ignore elif logging_integration == "gcs_bucket": for callback in _in_memory_loggers: if isinstance(callback, GCSBucketLogger): @@ -2372,6 +2377,10 @@ def get_custom_logger_compatible_class( for callback in _in_memory_loggers: if isinstance(callback, DataDogLogger): return callback + elif logging_integration == "datadog_llm_observability": + for callback in _in_memory_loggers: + if isinstance(callback, DataDogLLMObsLogger): + return callback elif logging_integration == "gcs_bucket": for callback in _in_memory_loggers: if isinstance(callback, GCSBucketLogger): diff --git a/litellm/types/integrations/datadog_llm_obs.py b/litellm/types/integrations/datadog_llm_obs.py new file mode 100644 index 0000000000..119d8ecc7a --- /dev/null +++ b/litellm/types/integrations/datadog_llm_obs.py @@ -0,0 +1,52 @@ +""" +Payloads for Datadog LLM Observability Service (LLMObs) + +API Reference: https://docs.datadoghq.com/llm_observability/setup/api/?tab=example#api-standards +""" + +from typing import Any, List, Literal, Optional, TypedDict + + +class InputMeta(TypedDict): + messages: List[Any] + + +class OutputMeta(TypedDict): + messages: List[Any] + + +class Meta(TypedDict): + # The span kind: "agent", "workflow", "llm", "tool", "task", "embedding", or "retrieval". + kind: Literal["llm", "tool", "task", "embedding", "retrieval"] + input: InputMeta # The span’s input information. + output: OutputMeta # The span’s output information. + + +class LLMMetrics(TypedDict, total=False): + input_tokens: float + output_tokens: float + total_tokens: float + time_to_first_token: float + time_per_output_token: float + + +class LLMObsPayload(TypedDict): + parent_id: str + trace_id: str + span_id: str + name: str + meta: Meta + start_ns: int + duration: int + metrics: LLMMetrics + + +class DDSpanAttributes(TypedDict): + ml_app: str + tags: List[str] + spans: List[LLMObsPayload] + + +class DDIntakePayload(TypedDict): + type: str + attributes: DDSpanAttributes diff --git a/tests/logging_callback_tests/test_datadog_llm_obs.py b/tests/logging_callback_tests/test_datadog_llm_obs.py new file mode 100644 index 0000000000..84ec3b2d91 --- /dev/null +++ b/tests/logging_callback_tests/test_datadog_llm_obs.py @@ -0,0 +1,141 @@ +""" +Test the DataDogLLMObsLogger +""" + +import io +import os +import sys + + +sys.path.insert(0, os.path.abspath("../..")) + +import asyncio +import gzip +import json +import logging +import time +from unittest.mock import AsyncMock, patch + +import pytest + +import litellm +from litellm import completion +from litellm._logging import verbose_logger +from litellm.integrations.datadog.datadog_llm_obs import DataDogLLMObsLogger +from datetime import datetime, timedelta +from litellm.types.integrations.datadog_llm_obs import * +from litellm.types.utils import ( + StandardLoggingPayload, + StandardLoggingModelInformation, + StandardLoggingMetadata, + StandardLoggingHiddenParams, +) + +verbose_logger.setLevel(logging.DEBUG) + + +def create_standard_logging_payload() -> StandardLoggingPayload: + return StandardLoggingPayload( + id="test_id", + call_type="completion", + response_cost=0.1, + response_cost_failure_debug_info=None, + status="success", + total_tokens=30, + prompt_tokens=20, + completion_tokens=10, + startTime=1234567890.0, + endTime=1234567891.0, + completionStartTime=1234567890.5, + model_map_information=StandardLoggingModelInformation( + model_map_key="gpt-3.5-turbo", model_map_value=None + ), + model="gpt-3.5-turbo", + model_id="model-123", + model_group="openai-gpt", + api_base="https://api.openai.com", + metadata=StandardLoggingMetadata( + user_api_key_hash="test_hash", + user_api_key_org_id=None, + user_api_key_alias="test_alias", + user_api_key_team_id="test_team", + user_api_key_user_id="test_user", + user_api_key_team_alias="test_team_alias", + spend_logs_metadata=None, + requester_ip_address="127.0.0.1", + requester_metadata=None, + ), + cache_hit=False, + cache_key=None, + saved_cache_cost=0.0, + request_tags=[], + end_user=None, + requester_ip_address="127.0.0.1", + messages=[{"role": "user", "content": "Hello, world!"}], + response={"choices": [{"message": {"content": "Hi there!"}}]}, + error_str=None, + model_parameters={"stream": True}, + hidden_params=StandardLoggingHiddenParams( + model_id="model-123", + cache_key=None, + api_base="https://api.openai.com", + response_cost="0.1", + additional_headers=None, + ), + ) + + +@pytest.mark.asyncio +async def test_datadog_llm_obs_logging(): + datadog_llm_obs_logger = DataDogLLMObsLogger() + litellm.callbacks = [datadog_llm_obs_logger] + litellm.set_verbose = True + + for _ in range(2): + response = await litellm.acompletion( + model="gpt-4o", messages=["Hello testing dd llm obs!"], mock_response="hi" + ) + + print(response) + + await asyncio.sleep(6) + + +@pytest.mark.asyncio +async def test_create_llm_obs_payload(): + datadog_llm_obs_logger = DataDogLLMObsLogger() + standard_logging_payload = create_standard_logging_payload() + payload = datadog_llm_obs_logger.create_llm_obs_payload( + kwargs={ + "model": "gpt-4", + "messages": [{"role": "user", "content": "Hello"}], + "standard_logging_object": standard_logging_payload, + }, + response_obj=litellm.ModelResponse( + id="test_id", + choices=[{"message": {"content": "Hi there!"}}], + created=12, + model="gpt-4", + ), + start_time=datetime.now(), + end_time=datetime.now() + timedelta(seconds=1), + ) + + print("dd created payload", payload) + + assert payload["name"] == "litellm_llm_call" + assert payload["meta"]["kind"] == "llm" + assert payload["meta"]["input"]["messages"] == [ + {"role": "user", "content": "Hello, world!"} + ] + assert payload["meta"]["output"]["messages"] == [ + { + "content": "Hi there!", + "role": "assistant", + "tool_calls": None, + "function_call": None, + } + ] + assert payload["metrics"]["input_tokens"] == 20 + assert payload["metrics"]["output_tokens"] == 10 + assert payload["metrics"]["total_tokens"] == 30 diff --git a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py index 5b14717dc1..ebc7dd33cb 100644 --- a/tests/logging_callback_tests/test_unit_tests_init_callbacks.py +++ b/tests/logging_callback_tests/test_unit_tests_init_callbacks.py @@ -27,6 +27,7 @@ from litellm.integrations.langsmith import LangsmithLogger from litellm.integrations.literal_ai import LiteralAILogger from litellm.integrations.prometheus import PrometheusLogger from litellm.integrations.datadog.datadog import DataDogLogger +from litellm.integrations.datadog.datadog_llm_obs import DataDogLLMObsLogger from litellm.integrations.gcs_bucket.gcs_bucket import GCSBucketLogger from litellm.integrations.opik.opik import OpikLogger from litellm.integrations.opentelemetry import OpenTelemetry @@ -49,6 +50,7 @@ callback_class_str_to_classType = { "literalai": LiteralAILogger, "prometheus": PrometheusLogger, "datadog": DataDogLogger, + "datadog_llm_observability": DataDogLLMObsLogger, "gcs_bucket": GCSBucketLogger, "opik": OpikLogger, "argilla": ArgillaLogger, From 828631d6fcad46685e261b5b850eb0623bdd0e33 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Mon, 28 Oct 2024 22:01:48 +0530 Subject: [PATCH 62/62] add pricing for amazon.titan-embed-image-v1 (#6444) --- litellm/model_prices_and_context_window_backup.json | 12 ++++++++++++ model_prices_and_context_window.json | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index fe8834dbb4..f682ce39da 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -4139,6 +4139,18 @@ "litellm_provider": "bedrock", "mode": "embedding" }, + "amazon.titan-embed-image-v1": { + "max_tokens": 128, + "max_input_tokens": 128, + "output_vector_size": 1024, + "input_cost_per_token": 0.0000008, + "input_cost_per_image": 0.00006, + "output_cost_per_token": 0.0, + "litellm_provider": "bedrock", + "supports_image_input": true, + "mode": "embedding", + "source": "https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/providers?model=amazon.titan-image-generator-v1" + }, "mistral.mistral-7b-instruct-v0:2": { "max_tokens": 8191, "max_input_tokens": 32000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index fe8834dbb4..f682ce39da 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -4139,6 +4139,18 @@ "litellm_provider": "bedrock", "mode": "embedding" }, + "amazon.titan-embed-image-v1": { + "max_tokens": 128, + "max_input_tokens": 128, + "output_vector_size": 1024, + "input_cost_per_token": 0.0000008, + "input_cost_per_image": 0.00006, + "output_cost_per_token": 0.0, + "litellm_provider": "bedrock", + "supports_image_input": true, + "mode": "embedding", + "source": "https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/providers?model=amazon.titan-image-generator-v1" + }, "mistral.mistral-7b-instruct-v0:2": { "max_tokens": 8191, "max_input_tokens": 32000,