From c0336d3f40deb3d3017e143ade99047f1c1980ae Mon Sep 17 00:00:00 2001 From: Emir Ayar Date: Sun, 31 Mar 2024 22:43:30 +0200 Subject: [PATCH 01/62] add a third condition: list of text-content dictionaries --- litellm/llms/prompt_templates/factory.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index 4492423f45d..30964f7329c 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -684,9 +684,15 @@ def anthropic_messages_pt(messages: list): assistant_content = [] ## MERGE CONSECUTIVE ASSISTANT CONTENT ## while msg_i < len(messages) and messages[msg_i]["role"] == "assistant": - assistant_text = ( - messages[msg_i].get("content") or "" - ) # either string or none + # Handle assistant messages as string, none, or list of text-content dictionaries. + if isinstance(messages[msg_i].get("content"), list): + assistant_text = '' + for content in messages[msg_i]["content"]: + if content.get("type") == "text": + assistant_text += content["text"] + else: + assistant_text = messages[msg_i].get("content") or "" + if messages[msg_i].get( "tool_calls", [] ): # support assistant tool invoke convertion From 6edb13373347dd74b5c9ede56c3cd37e0e4eab9c Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Sat, 20 Apr 2024 19:56:20 +0200 Subject: [PATCH 02/62] Added support for IBM watsonx.ai models --- litellm/__init__.py | 7 + litellm/llms/prompt_templates/factory.py | 44 +++ litellm/llms/watsonx.py | 480 +++++++++++++++++++++++ litellm/main.py | 38 ++ litellm/utils.py | 69 ++++ 5 files changed, 638 insertions(+) create mode 100644 litellm/llms/watsonx.py diff --git a/litellm/__init__.py b/litellm/__init__.py index b9d9891ca25..95dd33f1c87 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -298,6 +298,7 @@ aleph_alpha_models: List = [] bedrock_models: List = [] deepinfra_models: List = [] perplexity_models: List = [] +watsonx_models: List = [] for key, value in model_cost.items(): if value.get("litellm_provider") == "openai": open_ai_chat_completion_models.append(key) @@ -342,6 +343,8 @@ for key, value in model_cost.items(): deepinfra_models.append(key) elif value.get("litellm_provider") == "perplexity": perplexity_models.append(key) + elif value.get("litellm_provider") == "watsonx": + watsonx_models.append(key) # known openai compatible endpoints - we'll eventually move this list to the model_prices_and_context_window.json dictionary openai_compatible_endpoints: List = [ @@ -478,6 +481,7 @@ model_list = ( + perplexity_models + maritalk_models + vertex_language_models + + watsonx_models ) provider_list: List = [ @@ -516,6 +520,7 @@ provider_list: List = [ "cloudflare", "xinference", "fireworks_ai", + "watsonx", "custom", # custom apis ] @@ -537,6 +542,7 @@ models_by_provider: dict = { "deepinfra": deepinfra_models, "perplexity": perplexity_models, "maritalk": maritalk_models, + "watsonx": watsonx_models, } # mapping for those models which have larger equivalents @@ -650,6 +656,7 @@ from .llms.bedrock import ( ) from .llms.openai import OpenAIConfig, OpenAITextCompletionConfig from .llms.azure import AzureOpenAIConfig, AzureOpenAIError +from .llms.watsonx import IBMWatsonXConfig from .main import * # type: ignore from .integrations import * from .exceptions import ( diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index 176c81d5d66..8ebd2a38f47 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -416,6 +416,32 @@ def format_prompt_togetherai(messages, prompt_format, chat_template): prompt = default_pt(messages) return prompt +### IBM Granite + +def ibm_granite_pt(messages: list): + """ + IBM's Granite models uses the template: + <|system|> {system_message} <|user|> {user_message} <|assistant|> {assistant_message} + + See: https://www.ibm.com/docs/en/watsonx-as-a-service?topic=solutions-supported-foundation-models + """ + return custom_prompt( + messages=messages, + role_dict={ + 'system': { + 'pre_message': '<|system|>\n', + 'post_message': '\n', + }, + 'user': { + 'pre_message': '<|user|>\n', + 'post_message': '\n', + }, + 'assistant': { + 'pre_message': '<|assistant|>\n', + 'post_message': '\n', + } + } + ).strip() ### ANTHROPIC ### @@ -1327,6 +1353,24 @@ def prompt_factory( return messages elif custom_llm_provider == "azure_text": return azure_text_pt(messages=messages) + elif custom_llm_provider == "watsonx": + if "granite" in model and "chat" in model: + # granite-13b-chat-v1 and granite-13b-chat-v2 use a specific prompt template + return ibm_granite_pt(messages=messages) + elif "ibm-mistral" in model: + # models like ibm-mistral/mixtral-8x7b-instruct-v01-q use the mistral instruct prompt template + return mistral_instruct_pt(messages=messages) + elif "meta-llama/llama-3" in model and "instruct" in model: + return custom_prompt( + role_dict={ + "system": {"pre_message": "<|start_header_id|>system<|end_header_id|>\n", "post_message": "<|eot_id|>"}, + "user": {"pre_message": "<|start_header_id|>user<|end_header_id|>\n", "post_message": "<|eot_id|>"}, + "assistant": {"pre_message": "<|start_header_id|>assistant<|end_header_id|>\n", "post_message": "<|eot_id|>"}, + }, + messages=messages, + initial_prompt_value="<|begin_of_text|>", + # final_prompt_value="\n", + ) try: if "meta-llama/llama-2" in model and "chat" in model: return llama_2_chat_pt(messages=messages) diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py new file mode 100644 index 00000000000..7cb45730b26 --- /dev/null +++ b/litellm/llms/watsonx.py @@ -0,0 +1,480 @@ +import json, types, time +from typing import Callable, Optional, Any, Union, List + +import httpx +import litellm +from litellm.utils import ModelResponse, get_secret, Usage, ImageResponse + +from .prompt_templates import factory as ptf + +class WatsonxError(Exception): + def __init__(self, status_code, message): + self.status_code = status_code + self.message = message + self.request = httpx.Request( + method="POST", url="https://https://us-south.ml.cloud.ibm.com" + ) + self.response = httpx.Response(status_code=status_code, request=self.request) + super().__init__( + self.message + ) # Call the base class constructor with the parameters it needs + +class IBMWatsonXConfig: + """ + Reference: https://cloud.ibm.com/apidocs/watsonx-ai#deployments-text-generation + (See ibm_watsonx_ai.metanames.GenTextParamsMetaNames for a list of all available params) + + Supported params for all available watsonx.ai foundational models. + + - `decoding_method` (str): One of "greedy" or "sample" + + - `temperature` (float): Sets the model temperature for sampling - not available when decoding_method='greedy'. + + - `max_new_tokens` (integer): Maximum length of the generated tokens. + + - `min_new_tokens` (integer): Maximum length of input tokens. Any more than this will be truncated. + + - `stop_sequences` (string[]): list of strings to use as stop sequences. + + - `time_limit` (integer): time limit in milliseconds. If the generation is not completed within the time limit, the model will return the generated text up to that point. + + - `top_p` (integer): top p for sampling - not available when decoding_method='greedy'. + + - `top_k` (integer): top k for sampling - not available when decoding_method='greedy'. + + - `repetition_penalty` (float): token repetition penalty during text generation. + + - `stream` (bool): If True, the model will return a stream of responses. + + - `return_options` (dict): A dictionary of options to return. Options include "input_text", "generated_tokens", "input_tokens", "token_ranks". + + - `truncate_input_tokens` (integer): Truncate input tokens to this length. + + - `length_penalty` (dict): A dictionary with keys "decay_factor" and "start_index". + + - `random_seed` (integer): Random seed for text generation. + + - `guardrails` (bool): Enable guardrails for harmful content. + + - `guardrails_hap_params` (dict): Guardrails for harmful content. + + - `guardrails_pii_params` (dict): Guardrails for Personally Identifiable Information. + + - `concurrency_limit` (integer): Maximum number of concurrent requests. + + - `async_mode` (bool): Enable async mode. + + - `verify` (bool): Verify the SSL certificate of calls to the watsonx url. + + - `validate` (bool): Validate the model_id at initialization. + + - `model_inference` (ibm_watsonx_ai.ModelInference): An instance of an ibm_watsonx_ai.ModelInference class to use instead of creating a new model instance. + + - `watsonx_client` (ibm_watsonx_ai.APIClient): An instance of an ibm_watsonx_ai.APIClient class to initialize the watsonx model with. + """ + decoding_method: Optional[str] = "sample" # 'sample' or 'greedy'. "sample" follows the default openai API behavior + temperature: Optional[float] = None # + min_new_tokens: Optional[int] = None + max_new_tokens: Optional[int] = litellm.max_tokens + top_k: Optional[int] = None + top_p: Optional[float] = None + random_seed: Optional[int] = None # e.g 42 + repetition_penalty: Optional[float] = None + stop_sequences: Optional[List[str]] = None # e.g ["}", ")", "."] + time_limit: Optional[int] = None # e.g 10000 (timeout in milliseconds) + return_options: Optional[dict] = None # e.g {"input_text": True, "generated_tokens": True, "input_tokens": True, "token_ranks": False} + truncate_input_tokens: Optional[int] = None # e.g 512 + length_penalty: Optional[dict] = None # e.g {"decay_factor": 2.5, "start_index": 5} + stream: Optional[bool] = False + # other inference params + guardrails: Optional[bool] = False # enable guardrails + guardrails_hap_params: Optional[dict] = None # guardrails for harmful content + guardrails_pii_params: Optional[dict] = None # guardrails for Personally Identifiable Information + concurrency_limit: Optional[int] = 10 # max number of concurrent requests + async_mode: Optional[bool] = False # enable async mode + verify: Optional[Union[bool,str]] = None # verify the SSL certificate of calls to the watsonx url + validate: Optional[bool] = False # validate the model_id at initialization + model_inference: Optional[object] = None # an instance of an ibm_watsonx_ai.ModelInference class to use instead of creating a new model instance + watsonx_client: Optional[object] = None # an instance of an ibm_watsonx_ai.APIClient class to initialize the watsonx model with + + def __init__( + self, + decoding_method: Optional[str] = None, + temperature: Optional[float] = None, + min_new_tokens: Optional[int] = None, + max_new_tokens: Optional[ + int + ] = litellm.max_tokens, # petals requires max tokens to be set + top_k: Optional[int] = None, + top_p: Optional[float] = None, + random_seed: Optional[int] = None, + repetition_penalty: Optional[float] = None, + stop_sequences: Optional[List[str]] = None, + time_limit: Optional[int] = None, + return_options: Optional[dict] = None, + truncate_input_tokens: Optional[int] = None, + length_penalty: Optional[dict] = None, + stream: Optional[bool] = False, + guardrails: Optional[bool] = False, + guardrails_hap_params: Optional[dict] = None, + guardrails_pii_params: Optional[dict] = None, + concurrency_limit: Optional[int] = 10, + async_mode: Optional[bool] = False, + verify: Optional[Union[bool,str]] = None, + validate: Optional[bool] = False, + model_inference: Optional[object] = None, + watsonx_client: Optional[object] = None, + ) -> None: + locals_ = locals() + for key, value in locals_.items(): + if key != "self" and value is not None: + setattr(self.__class__, key, value) + + @classmethod + def get_config(cls): + return { + k: v + for k, v in cls.__dict__.items() + if not k.startswith("__") + and not isinstance( + v, + ( + types.FunctionType, + types.BuiltinFunctionType, + classmethod, + staticmethod, + ), + ) + and v is not None + } + + def get_supported_openai_params(self): + return [ + "temperature", # equivalent to temperature + "max_tokens", # equivalent to max_new_tokens + "top_p", # equivalent to top_p + "frequency_penalty", # equivalent to repetition_penalty + "stop", # equivalent to stop_sequences + "seed", # equivalent to random_seed + "stream", # equivalent to stream + ] + + +def init_watsonx_model( + model_id: str, + url: Optional[str] = None, + api_key: Optional[str] = None, + project_id: Optional[str] = None, + space_id: Optional[str] = None, + wx_credentials: Optional[dict] = None, + region_name: Optional[str] = None, + verify: Optional[Union[bool,str]] = None, + validate: Optional[bool] = False, + watsonx_client: Optional[object] = None, + model_params: Optional[dict] = None, +): + """ + Initialize a watsonx.ai model for inference. + + Args: + + model_id (str): The model ID to use for inference. If this is a model deployed in a deployment space, the model_id should be in the format 'deployment/' and the space_id to the deploymend space should be provided. + url (str): The URL of the watsonx.ai instance. + api_key (str): The API key for the watsonx.ai instance. + project_id (str): The project ID for the watsonx.ai instance. + space_id (str): The space ID for the deployment space. + wx_credentials (dict): A dictionary containing 'apikey' and 'url' keys for the watsonx.ai instance. + region_name (str): The region name for the watsonx.ai instance (e.g. 'us-south'). + verify (bool): Whether to verify the SSL certificate of calls to the watsonx url. + validate (bool): Whether to validate the model_id at initialization. + watsonx_client (object): An instance of the ibm_watsonx_ai.APIClient class. If this is provided, the model will be initialized using the provided client. + model_params (dict): A dictionary containing additional parameters to pass to the model (see IBMWatsonXConfig for a list of supported parameters). + """ + + from ibm_watsonx_ai import APIClient + from ibm_watsonx_ai.foundation_models import ModelInference + + + if wx_credentials is not None: + if 'apikey' not in wx_credentials and 'api_key' in wx_credentials: + wx_credentials['apikey'] = wx_credentials.pop('api_key') + if 'apikey' not in wx_credentials: + raise WatsonxError(500, "Error: key 'apikey' expected in wx_credentials") + + if url is None: + url = get_secret("WX_URL") or get_secret("WATSONX_URL") or get_secret("WML_URL") + if api_key is None: + api_key = get_secret("WX_API_KEY") or get_secret("WML_API_KEY") + if project_id is None: + project_id = get_secret("WX_PROJECT_ID") or get_secret("PROJECT_ID") + if region_name is None: + region_name = get_secret("WML_REGION_NAME") or get_secret("WX_REGION_NAME") or get_secret("REGION_NAME") + if space_id is None: + space_id = get_secret("WX_SPACE_ID") or get_secret("WML_DEPLOYMENT_SPACE_ID") or get_secret("SPACE_ID") + + + ## CHECK IS 'os.environ/' passed in + # Define the list of parameters to check + params_to_check = (url, api_key, project_id, space_id, region_name) + # Iterate over parameters and update if needed + for i, param in enumerate(params_to_check): + if param and param.startswith("os.environ/"): + params_to_check[i] = get_secret(param) + # Assign updated values back to parameters + url, api_key, project_id, space_id, region_name = params_to_check + + ### SET WATSONX URL + if url is not None or watsonx_client is not None or wx_credentials is not None: + pass + elif region_name is not None: + url = f"https://{region_name}.ml.cloud.ibm.com" + else: + raise WatsonxError( + message="Watsonx URL not set: set WX_URL env variable or in .env file", + status_code=401, + ) + if watsonx_client is not None and project_id is None: + project_id = watsonx_client.project_id + + if model_id.startswith("deployment/"): + # deployment models are passed in as 'deployment/' + assert space_id is not None, "space_id is required for deployment models" + deployment_id = '/'.join(model_id.split("/")[1:]) + model_id = None + else: + deployment_id = None + + if watsonx_client is not None: + model = ModelInference( + model_id=model_id, + params=model_params, + api_client=watsonx_client, + project_id=project_id, + deployment_id=deployment_id, + verify=verify, + validate=validate, + space_id=space_id, + ) + elif wx_credentials is not None: + model = ModelInference( + model_id=model_id, + params=model_params, + credentials=wx_credentials, + project_id=project_id, + deployment_id=deployment_id, + verify=verify, + validate=validate, + space_id=space_id, + ) + elif api_key is not None: + model = ModelInference( + model_id=model_id, + params=model_params, + credentials={ + "apikey": api_key, + "url": url, + }, + project_id=project_id, + deployment_id=deployment_id, + verify=verify, + validate=validate, + space_id=space_id, + ) + else: + raise WatsonxError(500, "WatsonX credentials not passed or could not be found.") + + + return model + + +def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): + # handle anthropic prompts and amazon titan prompts + if model in custom_prompt_dict: + # check if the model has a registered custom prompt + model_prompt_dict = custom_prompt_dict[model] + prompt = ptf.custom_prompt( + messages=messages, + role_dict=model_prompt_dict.get("role_dict", model_prompt_dict.get("roles")), + initial_prompt_value=model_prompt_dict.get("initial_prompt_value",""), + final_prompt_value=model_prompt_dict.get("final_prompt_value", ""), + bos_token=model_prompt_dict.get("bos_token", ""), + eos_token=model_prompt_dict.get("eos_token", ""), + ) + return prompt + elif provider == "ibm": + prompt = ptf.prompt_factory( + model=model, messages=messages, custom_llm_provider="watsonx" + ) + elif provider == "ibm-mistralai": + prompt = ptf.mistral_instruct_pt(messages=messages) + else: + prompt = ptf.prompt_factory(model=model, messages=messages, custom_llm_provider='watsonx') + return prompt + + +""" +IBM watsonx.ai AUTH Keys/Vars +os.environ['WX_URL'] = "" +os.environ['WX_API_KEY'] = "" +os.environ['WX_PROJECT_ID'] = "" +""" + +def completion( + model: str, + messages: list, + custom_prompt_dict: dict, + model_response: ModelResponse, + print_verbose: Callable, + encoding, + logging_obj, + optional_params:Optional[dict]=None, + litellm_params:Optional[dict]=None, + logger_fn=None, + timeout:float=None, +): + from ibm_watsonx_ai.foundation_models import Model, ModelInference + + try: + stream = optional_params.pop("stream", False) + extra_generate_params = dict( + guardrails=optional_params.pop("guardrails", False), + guardrails_hap_params=optional_params.pop("guardrails_hap_params", None), + guardrails_pii_params=optional_params.pop("guardrails_pii_params", None), + concurrency_limit=optional_params.pop("concurrency_limit", 10), + async_mode=optional_params.pop("async_mode", False), + ) + if timeout is not None and optional_params.get("time_limit") is None: + # the time_limit in watsonx.ai is in milliseconds (as opposed to OpenAI which is in seconds) + optional_params['time_limit'] = max(0, int(timeout*1000)) + extra_body_params = optional_params.pop("extra_body", {}) + optional_params.update(extra_body_params) + # LOAD CONFIG + config = IBMWatsonXConfig.get_config() + for k, v in config.items(): + if k not in optional_params: + optional_params[k] = v + + model_inference = optional_params.pop("model_inference", None) + if model_inference is None: + # INIT MODEL + model_client:ModelInference = init_watsonx_model( + model_id=model, + url=optional_params.pop("url", None), + api_key=optional_params.pop("api_key", None), + project_id=optional_params.pop("project_id", None), + space_id=optional_params.pop("space_id", None), + wx_credentials=optional_params.pop("wx_credentials", None), + region_name=optional_params.pop("region_name", None), + verify=optional_params.pop("verify", None), + validate=optional_params.pop("validate", False), + watsonx_client=optional_params.pop("watsonx_client", None), + model_params=optional_params, + ) + else: + model_client:ModelInference = model_inference + model = model_client.model_id + + # MAKE PROMPT + provider = model.split("/")[0] + model_name = '/'.join(model.split("/")[1:]) + prompt = convert_messages_to_prompt( + model, messages, provider, custom_prompt_dict + ) + ## COMPLETION CALL + if stream is True: + request_str = ( + "response = model.generate_text_stream(\n" + f"\tprompt={prompt},\n" + "\traw_response=True\n)" + ) + logging_obj.pre_call( + input=prompt, + api_key="", + additional_args={ + "complete_input_dict": optional_params, + "request_str": request_str, + }, + ) + # remove params that are not needed for streaming + del extra_generate_params["async_mode"] + del extra_generate_params["concurrency_limit"] + # make generate call + response = model_client.generate_text_stream( + prompt=prompt, + raw_response=True, + **extra_generate_params + ) + return litellm.CustomStreamWrapper( + response, + model=model, + custom_llm_provider="watsonx", + logging_obj=logging_obj, + ) + else: + try: + ## LOGGING + request_str = ( + "response = model.generate(\n" + f"\tprompt={prompt},\n" + "\traw_response=True\n)" + ) + logging_obj.pre_call( + input=prompt, + api_key="", + additional_args={ + "complete_input_dict": optional_params, + "request_str": request_str, + }, + ) + response = model_client.generate( + prompt=prompt, + **extra_generate_params + ) + except Exception as e: + raise WatsonxError(status_code=500, message=str(e)) + + ## LOGGING + logging_obj.post_call( + input=prompt, + api_key="", + original_response=json.dumps(response), + additional_args={"complete_input_dict": optional_params}, + ) + print_verbose(f"raw model_response: {response}") + ## BUILD RESPONSE OBJECT + output_text = response['results'][0]['generated_text'] + + try: + if ( + len(output_text) > 0 + and hasattr(model_response.choices[0], "message") + ): + model_response["choices"][0]["message"]["content"] = output_text + model_response["finish_reason"] = response['results'][0]['stop_reason'] + prompt_tokens = response['results'][0]['input_token_count'] + completion_tokens = response['results'][0]['generated_token_count'] + else: + raise Exception() + except: + raise WatsonxError( + message=json.dumps(output_text), + status_code=500, + ) + model_response['created'] = int(time.time()) + model_response['model'] = model_name + usage = Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ) + model_response.usage = usage + return model_response + except WatsonxError as e: + raise e + except Exception as e: + raise WatsonxError(status_code=500, message=str(e)) + + +def embedding(): + # logic for parsing in - calling - parsing out model embedding calls + pass \ No newline at end of file diff --git a/litellm/main.py b/litellm/main.py index 65696b3c0ce..753193f9658 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -63,6 +63,7 @@ from .llms import ( vertex_ai, vertex_ai_anthropic, maritalk, + watsonx, ) from .llms.openai import OpenAIChatCompletion, OpenAITextCompletion from .llms.azure import AzureChatCompletion @@ -1858,6 +1859,43 @@ def completion( ## RESPONSE OBJECT response = response + elif custom_llm_provider == "watsonx": + custom_prompt_dict = custom_prompt_dict or litellm.custom_prompt_dict + response = watsonx.completion( + model=model, + messages=messages, + custom_prompt_dict=custom_prompt_dict, + model_response=model_response, + print_verbose=print_verbose, + optional_params=optional_params, + litellm_params=litellm_params, + logger_fn=logger_fn, + encoding=encoding, + logging_obj=logging, + timeout=timeout, + ) + if ( + "stream" in optional_params + and optional_params["stream"] == True + and not isinstance(response, CustomStreamWrapper) + ): + # don't try to access stream object, + response = CustomStreamWrapper( + iter(response), + model, + custom_llm_provider="watsonx", + logging_obj=logging, + ) + + if optional_params.get("stream", False): + ## LOGGING + logging.post_call( + input=messages, + api_key=None, + original_response=response, + ) + ## RESPONSE OBJECT + response = response elif custom_llm_provider == "vllm": custom_prompt_dict = custom_prompt_dict or litellm.custom_prompt_dict model_response = vllm.completion( diff --git a/litellm/utils.py b/litellm/utils.py index e230675e68a..19118acbe36 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5331,6 +5331,45 @@ def get_optional_params( optional_params["extra_body"] = ( extra_body # openai client supports `extra_body` param ) + elif custom_llm_provider == "watsonx": + supported_params = get_supported_openai_params( + model=model, custom_llm_provider=custom_llm_provider + ) + _check_valid_arg(supported_params=supported_params) + if max_tokens is not None: + optional_params["max_new_tokens"] = max_tokens + if stream: + optional_params["stream"] = stream + if temperature is not None: + optional_params["temperature"] = temperature + if top_p is not None: + optional_params["top_p"] = top_p + if frequency_penalty is not None: + optional_params["repetition_penalty"] = frequency_penalty + if seed is not None: + optional_params["random_seed"] = seed + if stop is not None: + optional_params["stop_sequences"] = stop + + # WatsonX-only parameters + extra_body = {} + if "decoding_method" in passed_params: + extra_body["decoding_method"] = passed_params.pop("decoding_method") + if "min_tokens" in passed_params or "min_new_tokens" in passed_params: + extra_body["min_new_tokens"] = passed_params.pop("min_tokens", passed_params.pop("min_new_tokens")) + if "top_k" in passed_params: + extra_body["top_k"] = passed_params.pop("top_k") + if "truncate_input_tokens" in passed_params: + extra_body["truncate_input_tokens"] = passed_params.pop("truncate_input_tokens") + if "length_penalty" in passed_params: + extra_body["length_penalty"] = passed_params.pop("length_penalty") + if "time_limit" in passed_params: + extra_body["time_limit"] = passed_params.pop("time_limit") + if "return_options" in passed_params: + extra_body["return_options"] = passed_params.pop("return_options") + optional_params["extra_body"] = ( + extra_body # openai client supports `extra_body` param + ) else: # assume passing in params for openai/azure openai print_verbose( f"UNMAPPED PROVIDER, ASSUMING IT'S OPENAI/AZURE - model={model}, custom_llm_provider={custom_llm_provider}" @@ -5688,6 +5727,8 @@ def get_supported_openai_params(model: str, custom_llm_provider: str): "frequency_penalty", "presence_penalty", ] + elif custom_llm_provider == "watsonx": + return litellm.IBMWatsonXConfig().get_supported_openai_params() def get_formatted_prompt( @@ -5914,6 +5955,8 @@ def get_llm_provider( model in litellm.bedrock_models or model in litellm.bedrock_embedding_models ): custom_llm_provider = "bedrock" + elif model in litellm.watsonx_models: + custom_llm_provider = "watsonx" # openai embeddings elif model in litellm.open_ai_embedding_models: custom_llm_provider = "openai" @@ -9590,6 +9633,26 @@ class CustomStreamWrapper: "is_finished": chunk["is_finished"], "finish_reason": finish_reason, } + + def handle_watsonx_stream(self, chunk): + try: + if isinstance(chunk, dict): + pass + elif isinstance(chunk, str): + chunk = json.loads(chunk) + result = chunk.get("results", []) + if len(result) > 0: + text = result[0].get("generated_text", "") + finish_reason = result[0].get("stop_reason") + is_finished = finish_reason != 'not_finished' + return { + "text": text, + "is_finished": is_finished, + "finish_reason": finish_reason, + } + return "" + except Exception as e: + raise e def model_response_creator(self): model_response = ModelResponse(stream=True, model=self.model) @@ -9845,6 +9908,12 @@ class CustomStreamWrapper: print_verbose(f"completion obj content: {completion_obj['content']}") if response_obj["is_finished"]: self.received_finish_reason = response_obj["finish_reason"] + elif self.custom_llm_provider == "watsonx": + response_obj = self.handle_watsonx_stream(chunk) + completion_obj["content"] = response_obj["text"] + print_verbose(f"completion obj content: {completion_obj['content']}") + if response_obj["is_finished"]: + self.received_finish_reason = response_obj["finish_reason"] elif self.custom_llm_provider == "text-completion-openai": response_obj = self.handle_openai_text_completion_chunk(chunk) completion_obj["content"] = response_obj["text"] From ca0807d8ab723b881cda6c55a9168dbb1f5f2af4 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Sat, 20 Apr 2024 20:52:25 +0200 Subject: [PATCH 03/62] (docs) added watsonx cookbook --- cookbook/liteLLM_IBM_Watsonx.ipynb | 213 +++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 cookbook/liteLLM_IBM_Watsonx.ipynb diff --git a/cookbook/liteLLM_IBM_Watsonx.ipynb b/cookbook/liteLLM_IBM_Watsonx.ipynb new file mode 100644 index 00000000000..e62ec9c8c70 --- /dev/null +++ b/cookbook/liteLLM_IBM_Watsonx.ipynb @@ -0,0 +1,213 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LiteLLM x IBM [watsonx.ai](https://www.ibm.com/products/watsonx-ai)\n", + "\n", + "Note: For watsonx.ai requests you need to ensure you have `ibm-watsonx-ai` installed." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pre-Requisites" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install litellm\n", + "!pip install ibm-watsonx-ai" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set watsonx Credentials\n", + "\n", + "See [this documentation](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-credentials.html?context=wx) for more information about authenticating to watsonx.ai" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "os.environ[\"WX_URL\"] = \"\" # Your watsonx.ai base URL\n", + "os.environ[\"WX_API_KEY\"] = \"\" # Your IBM cloud API key or watsonx.ai token\n", + "os.environ[\"WX_PROJECT_ID\"] = \"\" # ID of your watsonx.ai project" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example Requests" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Granite v2 response:\n", + "ModelResponse(id='chatcmpl-afe4e875-2cfb-4e8c-aba5-36853007aaae', choices=[Choices(finish_reason='stop', index=0, message=Message(content=' I\\'m looking for a way to extract the email addresses from a CSV file. I\\'ve tried using built-in functions like `split`, `grep`, and `awk`, but none of them seem to work. Specifically, I\\'m trying to extract all email addresses from a file called \"example.csv\". Here\\'s what I have so far:\\n```bash\\ngrep -oP \"[\\\\w-]+@[a-z0-9-]+\\\\.[a-z]{2,}$\" example.csv > extracted_emails.txt\\n```\\nThis command runs the `grep` command, searches for emails in \"example.csv\", and saves the results to a new file called \"extracted\\\\_emails.txt\". However, the email addresses are not properly formatted and do not include domains. I think there might be a better way to do this, so I\\'m open to suggestions.\\n\\nAny help or guidance would be greatly appreciated.\\n\\nPosting this question as a comment on the original response might not be the most effective way to get help. If it\\'s possible, I can create a Code Review question here instead.\\n(Original post here: Date: Tue, 23 Apr 2024 11:53:38 +0200 Subject: [PATCH 04/62] feat - watsonx refractoring, removed dependency, and added support for embedding calls --- litellm/__init__.py | 2 +- litellm/llms/watsonx.py | 792 ++++++++++++++++++++++------------------ litellm/main.py | 11 +- litellm/utils.py | 38 +- 4 files changed, 477 insertions(+), 366 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index 95dd33f1c87..a7c17d53cca 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -656,7 +656,7 @@ from .llms.bedrock import ( ) from .llms.openai import OpenAIConfig, OpenAITextCompletionConfig from .llms.azure import AzureOpenAIConfig, AzureOpenAIError -from .llms.watsonx import IBMWatsonXConfig +from .llms.watsonx import IBMWatsonXAIConfig from .main import * # type: ignore from .integrations import * from .exceptions import ( diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index 7cb45730b26..38837ddb278 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -1,27 +1,31 @@ -import json, types, time -from typing import Callable, Optional, Any, Union, List +import json, types, time # noqa: E401 +from contextlib import contextmanager +from typing import Callable, Dict, Optional, Any, Union, List import httpx +import requests import litellm -from litellm.utils import ModelResponse, get_secret, Usage, ImageResponse +from litellm.utils import ModelResponse, get_secret, Usage +from .base import BaseLLM from .prompt_templates import factory as ptf -class WatsonxError(Exception): - def __init__(self, status_code, message): + +class WatsonXAIError(Exception): + def __init__(self, status_code, message, url: str = None): self.status_code = status_code self.message = message - self.request = httpx.Request( - method="POST", url="https://https://us-south.ml.cloud.ibm.com" - ) + url = url or "https://https://us-south.ml.cloud.ibm.com" + self.request = httpx.Request(method="POST", url=url) self.response = httpx.Response(status_code=status_code, request=self.request) super().__init__( self.message ) # Call the base class constructor with the parameters it needs -class IBMWatsonXConfig: + +class IBMWatsonXAIConfig: """ - Reference: https://cloud.ibm.com/apidocs/watsonx-ai#deployments-text-generation + Reference: https://cloud.ibm.com/apidocs/watsonx-ai#text-generation (See ibm_watsonx_ai.metanames.GenTextParamsMetaNames for a list of all available params) Supported params for all available watsonx.ai foundational models. @@ -34,96 +38,64 @@ class IBMWatsonXConfig: - `min_new_tokens` (integer): Maximum length of input tokens. Any more than this will be truncated. + - `length_penalty` (dict): A dictionary with keys "decay_factor" and "start_index". + - `stop_sequences` (string[]): list of strings to use as stop sequences. - - `time_limit` (integer): time limit in milliseconds. If the generation is not completed within the time limit, the model will return the generated text up to that point. - - - `top_p` (integer): top p for sampling - not available when decoding_method='greedy'. - - `top_k` (integer): top k for sampling - not available when decoding_method='greedy'. + - `top_p` (integer): top p for sampling - not available when decoding_method='greedy'. + - `repetition_penalty` (float): token repetition penalty during text generation. - - `stream` (bool): If True, the model will return a stream of responses. - - - `return_options` (dict): A dictionary of options to return. Options include "input_text", "generated_tokens", "input_tokens", "token_ranks". - - `truncate_input_tokens` (integer): Truncate input tokens to this length. - - `length_penalty` (dict): A dictionary with keys "decay_factor" and "start_index". + - `include_stop_sequences` (bool): If True, the stop sequence will be included at the end of the generated text in the case of a match. + + - `return_options` (dict): A dictionary of options to return. Options include "input_text", "generated_tokens", "input_tokens", "token_ranks". Values are boolean. - `random_seed` (integer): Random seed for text generation. - - `guardrails` (bool): Enable guardrails for harmful content. + - `moderations` (dict): Dictionary of properties that control the moderations, for usages such as Hate and profanity (HAP) and PII filtering. - - `guardrails_hap_params` (dict): Guardrails for harmful content. - - - `guardrails_pii_params` (dict): Guardrails for Personally Identifiable Information. - - - `concurrency_limit` (integer): Maximum number of concurrent requests. - - - `async_mode` (bool): Enable async mode. - - - `verify` (bool): Verify the SSL certificate of calls to the watsonx url. - - - `validate` (bool): Validate the model_id at initialization. - - - `model_inference` (ibm_watsonx_ai.ModelInference): An instance of an ibm_watsonx_ai.ModelInference class to use instead of creating a new model instance. - - - `watsonx_client` (ibm_watsonx_ai.APIClient): An instance of an ibm_watsonx_ai.APIClient class to initialize the watsonx model with. + - `stream` (bool): If True, the model will return a stream of responses. """ - decoding_method: Optional[str] = "sample" # 'sample' or 'greedy'. "sample" follows the default openai API behavior - temperature: Optional[float] = None # + + decoding_method: Optional[str] = "sample" + temperature: Optional[float] = None + max_new_tokens: Optional[int] = None # litellm.max_tokens min_new_tokens: Optional[int] = None - max_new_tokens: Optional[int] = litellm.max_tokens + length_penalty: Optional[dict] = None # e.g {"decay_factor": 2.5, "start_index": 5} + stop_sequences: Optional[List[str]] = None # e.g ["}", ")", "."] top_k: Optional[int] = None top_p: Optional[float] = None - random_seed: Optional[int] = None # e.g 42 repetition_penalty: Optional[float] = None - stop_sequences: Optional[List[str]] = None # e.g ["}", ")", "."] - time_limit: Optional[int] = None # e.g 10000 (timeout in milliseconds) - return_options: Optional[dict] = None # e.g {"input_text": True, "generated_tokens": True, "input_tokens": True, "token_ranks": False} - truncate_input_tokens: Optional[int] = None # e.g 512 - length_penalty: Optional[dict] = None # e.g {"decay_factor": 2.5, "start_index": 5} + truncate_input_tokens: Optional[int] = None + include_stop_sequences: Optional[bool] = False + return_options: Optional[dict] = None + return_options: Optional[Dict[str, bool]] = None + random_seed: Optional[int] = None # e.g 42 + moderations: Optional[dict] = None stream: Optional[bool] = False - # other inference params - guardrails: Optional[bool] = False # enable guardrails - guardrails_hap_params: Optional[dict] = None # guardrails for harmful content - guardrails_pii_params: Optional[dict] = None # guardrails for Personally Identifiable Information - concurrency_limit: Optional[int] = 10 # max number of concurrent requests - async_mode: Optional[bool] = False # enable async mode - verify: Optional[Union[bool,str]] = None # verify the SSL certificate of calls to the watsonx url - validate: Optional[bool] = False # validate the model_id at initialization - model_inference: Optional[object] = None # an instance of an ibm_watsonx_ai.ModelInference class to use instead of creating a new model instance - watsonx_client: Optional[object] = None # an instance of an ibm_watsonx_ai.APIClient class to initialize the watsonx model with def __init__( self, decoding_method: Optional[str] = None, temperature: Optional[float] = None, + max_new_tokens: Optional[int] = None, min_new_tokens: Optional[int] = None, - max_new_tokens: Optional[ - int - ] = litellm.max_tokens, # petals requires max tokens to be set + length_penalty: Optional[dict] = None, + stop_sequences: Optional[List[str]] = None, top_k: Optional[int] = None, top_p: Optional[float] = None, - random_seed: Optional[int] = None, repetition_penalty: Optional[float] = None, - stop_sequences: Optional[List[str]] = None, - time_limit: Optional[int] = None, - return_options: Optional[dict] = None, truncate_input_tokens: Optional[int] = None, - length_penalty: Optional[dict] = None, - stream: Optional[bool] = False, - guardrails: Optional[bool] = False, - guardrails_hap_params: Optional[dict] = None, - guardrails_pii_params: Optional[dict] = None, - concurrency_limit: Optional[int] = 10, - async_mode: Optional[bool] = False, - verify: Optional[Union[bool,str]] = None, - validate: Optional[bool] = False, - model_inference: Optional[object] = None, - watsonx_client: Optional[object] = None, + include_stop_sequences: Optional[bool] = None, + return_options: Optional[dict] = None, + random_seed: Optional[int] = None, + moderations: Optional[dict] = None, + stream: Optional[bool] = None, + **kwargs, ) -> None: locals_ = locals() for key, value in locals_.items(): @@ -150,143 +122,16 @@ class IBMWatsonXConfig: def get_supported_openai_params(self): return [ - "temperature", # equivalent to temperature - "max_tokens", # equivalent to max_new_tokens - "top_p", # equivalent to top_p - "frequency_penalty", # equivalent to repetition_penalty - "stop", # equivalent to stop_sequences - "seed", # equivalent to random_seed - "stream", # equivalent to stream + "temperature", # equivalent to temperature + "max_tokens", # equivalent to max_new_tokens + "top_p", # equivalent to top_p + "frequency_penalty", # equivalent to repetition_penalty + "stop", # equivalent to stop_sequences + "seed", # equivalent to random_seed + "stream", # equivalent to stream ] -def init_watsonx_model( - model_id: str, - url: Optional[str] = None, - api_key: Optional[str] = None, - project_id: Optional[str] = None, - space_id: Optional[str] = None, - wx_credentials: Optional[dict] = None, - region_name: Optional[str] = None, - verify: Optional[Union[bool,str]] = None, - validate: Optional[bool] = False, - watsonx_client: Optional[object] = None, - model_params: Optional[dict] = None, -): - """ - Initialize a watsonx.ai model for inference. - - Args: - - model_id (str): The model ID to use for inference. If this is a model deployed in a deployment space, the model_id should be in the format 'deployment/' and the space_id to the deploymend space should be provided. - url (str): The URL of the watsonx.ai instance. - api_key (str): The API key for the watsonx.ai instance. - project_id (str): The project ID for the watsonx.ai instance. - space_id (str): The space ID for the deployment space. - wx_credentials (dict): A dictionary containing 'apikey' and 'url' keys for the watsonx.ai instance. - region_name (str): The region name for the watsonx.ai instance (e.g. 'us-south'). - verify (bool): Whether to verify the SSL certificate of calls to the watsonx url. - validate (bool): Whether to validate the model_id at initialization. - watsonx_client (object): An instance of the ibm_watsonx_ai.APIClient class. If this is provided, the model will be initialized using the provided client. - model_params (dict): A dictionary containing additional parameters to pass to the model (see IBMWatsonXConfig for a list of supported parameters). - """ - - from ibm_watsonx_ai import APIClient - from ibm_watsonx_ai.foundation_models import ModelInference - - - if wx_credentials is not None: - if 'apikey' not in wx_credentials and 'api_key' in wx_credentials: - wx_credentials['apikey'] = wx_credentials.pop('api_key') - if 'apikey' not in wx_credentials: - raise WatsonxError(500, "Error: key 'apikey' expected in wx_credentials") - - if url is None: - url = get_secret("WX_URL") or get_secret("WATSONX_URL") or get_secret("WML_URL") - if api_key is None: - api_key = get_secret("WX_API_KEY") or get_secret("WML_API_KEY") - if project_id is None: - project_id = get_secret("WX_PROJECT_ID") or get_secret("PROJECT_ID") - if region_name is None: - region_name = get_secret("WML_REGION_NAME") or get_secret("WX_REGION_NAME") or get_secret("REGION_NAME") - if space_id is None: - space_id = get_secret("WX_SPACE_ID") or get_secret("WML_DEPLOYMENT_SPACE_ID") or get_secret("SPACE_ID") - - - ## CHECK IS 'os.environ/' passed in - # Define the list of parameters to check - params_to_check = (url, api_key, project_id, space_id, region_name) - # Iterate over parameters and update if needed - for i, param in enumerate(params_to_check): - if param and param.startswith("os.environ/"): - params_to_check[i] = get_secret(param) - # Assign updated values back to parameters - url, api_key, project_id, space_id, region_name = params_to_check - - ### SET WATSONX URL - if url is not None or watsonx_client is not None or wx_credentials is not None: - pass - elif region_name is not None: - url = f"https://{region_name}.ml.cloud.ibm.com" - else: - raise WatsonxError( - message="Watsonx URL not set: set WX_URL env variable or in .env file", - status_code=401, - ) - if watsonx_client is not None and project_id is None: - project_id = watsonx_client.project_id - - if model_id.startswith("deployment/"): - # deployment models are passed in as 'deployment/' - assert space_id is not None, "space_id is required for deployment models" - deployment_id = '/'.join(model_id.split("/")[1:]) - model_id = None - else: - deployment_id = None - - if watsonx_client is not None: - model = ModelInference( - model_id=model_id, - params=model_params, - api_client=watsonx_client, - project_id=project_id, - deployment_id=deployment_id, - verify=verify, - validate=validate, - space_id=space_id, - ) - elif wx_credentials is not None: - model = ModelInference( - model_id=model_id, - params=model_params, - credentials=wx_credentials, - project_id=project_id, - deployment_id=deployment_id, - verify=verify, - validate=validate, - space_id=space_id, - ) - elif api_key is not None: - model = ModelInference( - model_id=model_id, - params=model_params, - credentials={ - "apikey": api_key, - "url": url, - }, - project_id=project_id, - deployment_id=deployment_id, - verify=verify, - validate=validate, - space_id=space_id, - ) - else: - raise WatsonxError(500, "WatsonX credentials not passed or could not be found.") - - - return model - - def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): # handle anthropic prompts and amazon titan prompts if model in custom_prompt_dict: @@ -294,8 +139,10 @@ def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): model_prompt_dict = custom_prompt_dict[model] prompt = ptf.custom_prompt( messages=messages, - role_dict=model_prompt_dict.get("role_dict", model_prompt_dict.get("roles")), - initial_prompt_value=model_prompt_dict.get("initial_prompt_value",""), + role_dict=model_prompt_dict.get( + "role_dict", model_prompt_dict.get("roles") + ), + initial_prompt_value=model_prompt_dict.get("initial_prompt_value", ""), final_prompt_value=model_prompt_dict.get("final_prompt_value", ""), bos_token=model_prompt_dict.get("bos_token", ""), eos_token=model_prompt_dict.get("eos_token", ""), @@ -308,173 +155,408 @@ def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): elif provider == "ibm-mistralai": prompt = ptf.mistral_instruct_pt(messages=messages) else: - prompt = ptf.prompt_factory(model=model, messages=messages, custom_llm_provider='watsonx') + prompt = ptf.prompt_factory( + model=model, messages=messages, custom_llm_provider="watsonx" + ) return prompt -""" -IBM watsonx.ai AUTH Keys/Vars -os.environ['WX_URL'] = "" -os.environ['WX_API_KEY'] = "" -os.environ['WX_PROJECT_ID'] = "" -""" +class IBMWatsonXAI(BaseLLM): + """ + Class to interface with IBM Watsonx.ai API for text generation and embeddings. -def completion( - model: str, - messages: list, - custom_prompt_dict: dict, - model_response: ModelResponse, - print_verbose: Callable, - encoding, - logging_obj, - optional_params:Optional[dict]=None, - litellm_params:Optional[dict]=None, - logger_fn=None, - timeout:float=None, -): - from ibm_watsonx_ai.foundation_models import Model, ModelInference + Reference: https://cloud.ibm.com/apidocs/watsonx-ai + """ - try: - stream = optional_params.pop("stream", False) - extra_generate_params = dict( - guardrails=optional_params.pop("guardrails", False), - guardrails_hap_params=optional_params.pop("guardrails_hap_params", None), - guardrails_pii_params=optional_params.pop("guardrails_pii_params", None), - concurrency_limit=optional_params.pop("concurrency_limit", 10), - async_mode=optional_params.pop("async_mode", False), - ) - if timeout is not None and optional_params.get("time_limit") is None: - # the time_limit in watsonx.ai is in milliseconds (as opposed to OpenAI which is in seconds) - optional_params['time_limit'] = max(0, int(timeout*1000)) + api_version = "2024-03-13" + _text_gen_endpoint = "/ml/v1/text/generation" + _text_gen_stream_endpoint = "/ml/v1/text/generation_stream" + _deployment_text_gen_endpoint = "/ml/v1/deployments/{deployment_id}/text/generation" + _deployment_text_gen_stream_endpoint = ( + "/ml/v1/deployments/{deployment_id}/text/generation_stream" + ) + _embeddings_endpoint = "/ml/v1/text/embeddings" + _prompts_endpoint = "/ml/v1/prompts" + + def __init__(self) -> None: + super().__init__() + + def _prepare_text_generation_req( + self, + model_id: str, + prompt: str, + stream: bool, + optional_params: dict, + print_verbose: Callable = None, + ) -> httpx.Request: + """ + Get the request parameters for text generation. + """ + api_params = self._get_api_params(optional_params, print_verbose=print_verbose) + # build auth headers + api_token = api_params.get("token") + + headers = { + "Authorization": f"Bearer {api_token}", + "Content-Type": "application/json", + "Accept": "application/json", + } extra_body_params = optional_params.pop("extra_body", {}) optional_params.update(extra_body_params) - # LOAD CONFIG - config = IBMWatsonXConfig.get_config() + # init the payload to the text generation call + payload = { + "input": prompt, + "moderations": optional_params.pop("moderations", {}), + "parameters": optional_params, + } + request_params = dict(version=api_params["api_version"]) + # text generation endpoint deployment or model / stream or not + if model_id.startswith("deployment/"): + # deployment models are passed in as 'deployment/' + if api_params.get("space_id") is None: + raise WatsonXAIError( + status_code=401, + url=api_params["url"], + message="Error: space_id is required for models called using the 'deployment/' endpoint. Pass in the space_id as a parameter or set it in the WX_SPACE_ID environment variable.", + ) + deployment_id = "/".join(model_id.split("/")[1:]) + endpoint = ( + self._deployment_text_gen_stream_endpoint + if stream + else self._deployment_text_gen_endpoint + ) + endpoint = endpoint.format(deployment_id=deployment_id) + else: + payload["model_id"] = model_id + payload["project_id"] = api_params["project_id"] + endpoint = ( + self._text_gen_stream_endpoint if stream else self._text_gen_endpoint + ) + url = api_params["url"].rstrip("/") + endpoint + return httpx.Request( + "POST", url, headers=headers, json=payload, params=request_params + ) + + def _get_api_params(self, params: dict, print_verbose: Callable = None) -> dict: + """ + Find watsonx.ai credentials in the params or environment variables and return the headers for authentication. + """ + # Load auth variables from params + url = params.pop("url", None) + api_key = params.pop("apikey", None) + token = params.pop("token", None) + project_id = params.pop("project_id", None) # watsonx.ai project_id + space_id = params.pop("space_id", None) # watsonx.ai deployment space_id + region_name = params.pop("region_name", params.pop("region", None)) + wx_credentials = params.pop("wx_credentials", None) + api_version = params.pop("api_version", IBMWatsonXAI.api_version) + # Load auth variables from environment variables + if url is None: + url = ( + get_secret("WATSONX_URL") + or get_secret("WX_URL") + or get_secret("WML_URL") + ) + if api_key is None: + api_key = get_secret("WATSONX_API_KEY") or get_secret("WX_API_KEY") + if token is None: + token = get_secret("WATSONX_TOKEN") or get_secret("WX_TOKEN") + if project_id is None: + project_id = ( + get_secret("WATSONX_PROJECT_ID") + or get_secret("WX_PROJECT_ID") + or get_secret("PROJECT_ID") + ) + if region_name is None: + region_name = ( + get_secret("WATSONX_REGION") + or get_secret("WX_REGION") + or get_secret("REGION") + ) + if space_id is None: + space_id = ( + get_secret("WATSONX_DEPLOYMENT_SPACE_ID") + or get_secret("WATSONX_SPACE_ID") + or get_secret("WX_SPACE_ID") + or get_secret("SPACE_ID") + ) + + # credentials parsing + if wx_credentials is not None: + url = wx_credentials.get("url", url) + api_key = wx_credentials.get( + "apikey", wx_credentials.get("api_key", api_key) + ) + token = wx_credentials.get("token", token) + + # verify that all required credentials are present + if url is None: + raise WatsonXAIError( + status_code=401, + message="Error: Watsonx URL not set. Set WX_URL in environment variables or pass in as a parameter.", + ) + if token is None and api_key is not None: + # generate the auth token + if print_verbose: + print_verbose("Generating IAM token for Watsonx.ai") + token = self.generate_iam_token(api_key) + elif token is None and api_key is None: + raise WatsonXAIError( + status_code=401, + url=url, + message="Error: API key or token not found. Set WX_API_KEY or WX_TOKEN in environment variables or pass in as a parameter.", + ) + if project_id is None: + raise WatsonXAIError( + status_code=401, + url=url, + message="Error: Watsonx project_id not set. Set WX_PROJECT_ID in environment variables or pass in as a parameter.", + ) + + return { + "url": url, + "api_key": api_key, + "token": token, + "project_id": project_id, + "space_id": space_id, + "region_name": region_name, + "api_version": api_version, + } + + def completion( + self, + model: str, + messages: list, + custom_prompt_dict: dict, + model_response: ModelResponse, + print_verbose: Callable, + encoding, + logging_obj, + optional_params: Optional[dict] = None, + litellm_params: Optional[dict] = None, + logger_fn=None, + timeout: float = None, + ): + """ + Send a text generation request to the IBM Watsonx.ai API. + Reference: https://cloud.ibm.com/apidocs/watsonx-ai#text-generation + """ + stream = optional_params.pop("stream", False) + + # Load default configs + config = IBMWatsonXAIConfig.get_config() for k, v in config.items(): if k not in optional_params: optional_params[k] = v - model_inference = optional_params.pop("model_inference", None) - if model_inference is None: - # INIT MODEL - model_client:ModelInference = init_watsonx_model( - model_id=model, - url=optional_params.pop("url", None), - api_key=optional_params.pop("api_key", None), - project_id=optional_params.pop("project_id", None), - space_id=optional_params.pop("space_id", None), - wx_credentials=optional_params.pop("wx_credentials", None), - region_name=optional_params.pop("region_name", None), - verify=optional_params.pop("verify", None), - validate=optional_params.pop("validate", False), - watsonx_client=optional_params.pop("watsonx_client", None), - model_params=optional_params, - ) - else: - model_client:ModelInference = model_inference - model = model_client.model_id - - # MAKE PROMPT + # Make prompt to send to model provider = model.split("/")[0] - model_name = '/'.join(model.split("/")[1:]) + # model_name = "/".join(model.split("/")[1:]) prompt = convert_messages_to_prompt( model, messages, provider, custom_prompt_dict ) - ## COMPLETION CALL - if stream is True: - request_str = ( - "response = model.generate_text_stream(\n" - f"\tprompt={prompt},\n" - "\traw_response=True\n)" - ) - logging_obj.pre_call( - input=prompt, - api_key="", - additional_args={ - "complete_input_dict": optional_params, - "request_str": request_str, - }, - ) - # remove params that are not needed for streaming - del extra_generate_params["async_mode"] - del extra_generate_params["concurrency_limit"] - # make generate call - response = model_client.generate_text_stream( - prompt=prompt, - raw_response=True, - **extra_generate_params - ) - return litellm.CustomStreamWrapper( - response, - model=model, - custom_llm_provider="watsonx", - logging_obj=logging_obj, - ) - else: - try: - ## LOGGING - request_str = ( - "response = model.generate(\n" - f"\tprompt={prompt},\n" - "\traw_response=True\n)" - ) - logging_obj.pre_call( - input=prompt, - api_key="", - additional_args={ - "complete_input_dict": optional_params, - "request_str": request_str, - }, - ) - response = model_client.generate( - prompt=prompt, - **extra_generate_params - ) - except Exception as e: - raise WatsonxError(status_code=500, message=str(e)) - ## LOGGING - logging_obj.post_call( - input=prompt, - api_key="", - original_response=json.dumps(response), - additional_args={"complete_input_dict": optional_params}, - ) - print_verbose(f"raw model_response: {response}") - ## BUILD RESPONSE OBJECT - output_text = response['results'][0]['generated_text'] + def process_text_request(request: httpx.Request) -> ModelResponse: + with self._manage_response( + request, logging_obj=logging_obj, input=prompt, timeout=timeout + ) as resp: + json_resp = resp.json() + + generated_text = json_resp["results"][0]["generated_text"] + prompt_tokens = json_resp["results"][0]["input_token_count"] + completion_tokens = json_resp["results"][0]["generated_token_count"] + model_response["choices"][0]["message"]["content"] = generated_text + model_response["finish_reason"] = json_resp["results"][0]["stop_reason"] + model_response["created"] = int(time.time()) + model_response["model"] = model + model_response.usage = Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ) + return model_response + + def process_stream_request( + request: httpx.Request, + ) -> litellm.CustomStreamWrapper: + # stream the response - generated chunks will be handled + # by litellm.utils.CustomStreamWrapper.handle_watsonx_stream + with self._manage_response( + request, + logging_obj=logging_obj, + stream=True, + input=prompt, + timeout=timeout, + ) as resp: + response = litellm.CustomStreamWrapper( + resp.iter_lines(), + model=model, + custom_llm_provider="watsonx", + logging_obj=logging_obj, + ) + return response try: - if ( - len(output_text) > 0 - and hasattr(model_response.choices[0], "message") - ): - model_response["choices"][0]["message"]["content"] = output_text - model_response["finish_reason"] = response['results'][0]['stop_reason'] - prompt_tokens = response['results'][0]['input_token_count'] - completion_tokens = response['results'][0]['generated_token_count'] - else: - raise Exception() - except: - raise WatsonxError( - message=json.dumps(output_text), - status_code=500, + ## Get the response from the model + request = self._prepare_text_generation_req( + model_id=model, + prompt=prompt, + stream=stream, + optional_params=optional_params, + print_verbose=print_verbose, ) - model_response['created'] = int(time.time()) - model_response['model'] = model_name - usage = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + if stream: + return process_stream_request(request) + else: + return process_text_request(request) + except WatsonXAIError as e: + raise e + except Exception as e: + raise WatsonXAIError(status_code=500, message=str(e)) + + def embedding( + self, + model: str, + input: Union[list, str], + api_key: Optional[str] = None, + logging_obj=None, + model_response=None, + optional_params=None, + encoding=None, + ): + """ + Send a text embedding request to the IBM Watsonx.ai API. + """ + if optional_params is None: + optional_params = {} + # Load default configs + config = IBMWatsonXAIConfig.get_config() + for k, v in config.items(): + if k not in optional_params: + optional_params[k] = v + + # Load auth variables from environment variables + if isinstance(input, str): + input = [input] + if api_key is not None: + optional_params["api_key"] = api_key + api_params = self._get_api_params(optional_params) + # build auth headers + api_token = api_params.get("token") + headers = { + "Authorization": f"Bearer {api_token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + # init the payload to the text generation call + payload = { + "inputs": input, + "model_id": model, + "project_id": api_params["project_id"], + "parameters": optional_params, + } + request_params = dict(version=api_params["api_version"]) + url = api_params["url"].rstrip("/") + self._embeddings_endpoint + request = httpx.Request( + "POST", url, headers=headers, json=payload, params=request_params + ) + with self._manage_response( + request, logging_obj=logging_obj, input=input + ) as resp: + json_resp = resp.json() + + results = json_resp.get("results", []) + embedding_response = [] + for idx, result in enumerate(results): + embedding_response.append( + {"object": "embedding", "index": idx, "embedding": result["embedding"]} + ) + model_response["object"] = "list" + model_response["data"] = embedding_response + model_response["model"] = model + input_tokens = json_resp.get("input_token_count", 0) + model_response.usage = Usage( + prompt_tokens=input_tokens, completion_tokens=0, total_tokens=input_tokens ) - model_response.usage = usage return model_response - except WatsonxError as e: - raise e - except Exception as e: - raise WatsonxError(status_code=500, message=str(e)) + def generate_iam_token(self, api_key=None, **params): + headers = {} + headers["Content-Type"] = "application/x-www-form-urlencoded" + if api_key is None: + api_key = get_secret("WX_API_KEY") or get_secret("WATSONX_API_KEY") + if api_key is None: + raise ValueError("API key is required") + headers["Accept"] = "application/json" + data = { + "grant_type": "urn:ibm:params:oauth:grant-type:apikey", + "apikey": api_key, + } + response = httpx.post( + "https://iam.cloud.ibm.com/identity/token", data=data, headers=headers + ) + response.raise_for_status() + json_data = response.json() + iam_access_token = json_data["access_token"] + self.token = iam_access_token + return iam_access_token -def embedding(): - # logic for parsing in - calling - parsing out model embedding calls - pass \ No newline at end of file + @contextmanager + def _manage_response( + self, + request: httpx.Request, + logging_obj: Any, + stream: bool = False, + input: Optional[Any] = None, + timeout: float = None, + ): + request_str = ( + f"response = {request.method}(\n" + f"\turl={request.url},\n" + f"\tjson={request.content.decode()},\n" + f")" + ) + json_input = json.loads(request.content.decode()) + headers = dict(request.headers) + logging_obj.pre_call( + input=input, + api_key=request.headers.get("Authorization"), + additional_args={ + "complete_input_dict": json_input, + "request_str": request_str, + }, + ) + try: + if stream: + resp = requests.request( + method=request.method, + url=str(request.url), + headers=headers, + json=json_input, + stream=True, + timeout=timeout, + ) + # resp.raise_for_status() + yield resp + else: + resp = requests.request( + method=request.method, + url=str(request.url), + headers=headers, + json=json_input, + timeout=timeout, + ) + resp.raise_for_status() + yield resp + except Exception as e: + raise WatsonXAIError(status_code=500, message=str(e)) + if not stream: + logging_obj.post_call( + input=input, + api_key=request.headers.get("Authorization"), + original_response=json.dumps(resp.json()), + additional_args={ + "status_code": resp.status_code, + "complete_input_dict": request, + }, + ) diff --git a/litellm/main.py b/litellm/main.py index b61df8c12ee..8f357b83478 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1862,7 +1862,7 @@ def completion( response = response elif custom_llm_provider == "watsonx": custom_prompt_dict = custom_prompt_dict or litellm.custom_prompt_dict - response = watsonx.completion( + response = watsonx.IBMWatsonXAI().completion( model=model, messages=messages, custom_prompt_dict=custom_prompt_dict, @@ -2976,6 +2976,15 @@ def embedding( client=client, aembedding=aembedding, ) + elif custom_llm_provider == "watsonx": + response = watsonx.IBMWatsonXAI().embedding( + model=model, + input=input, + encoding=encoding, + logging_obj=logging, + optional_params=optional_params, + model_response=EmbeddingResponse(), + ) else: args = locals() raise ValueError(f"No valid embedding model args passed in - {args}") diff --git a/litellm/utils.py b/litellm/utils.py index 836587fb1d2..89061c3bfe5 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5771,7 +5771,7 @@ def get_supported_openai_params(model: str, custom_llm_provider: str): "presence_penalty", ] elif custom_llm_provider == "watsonx": - return litellm.IBMWatsonXConfig().get_supported_openai_params() + return litellm.IBMWatsonXAIConfig().get_supported_openai_params() def get_formatted_prompt( @@ -9682,20 +9682,31 @@ class CustomStreamWrapper: def handle_watsonx_stream(self, chunk): try: if isinstance(chunk, dict): - pass - elif isinstance(chunk, str): - chunk = json.loads(chunk) - result = chunk.get("results", []) - if len(result) > 0: - text = result[0].get("generated_text", "") - finish_reason = result[0].get("stop_reason") + parsed_response = chunk + elif isinstance(chunk, (str, bytes)): + if isinstance(chunk, bytes): + chunk = chunk.decode("utf-8") + if 'generated_text' in chunk: + response = chunk.replace('data: ', '').strip() + parsed_response = json.loads(response) + else: + return {"text": "", "is_finished": False} + else: + print_verbose(f"chunk: {chunk} (Type: {type(chunk)})") + raise ValueError(f"Unable to parse response. Original response: {chunk}") + results = parsed_response.get("results", []) + if len(results) > 0: + text = results[0].get("generated_text", "") + finish_reason = results[0].get("stop_reason") is_finished = finish_reason != 'not_finished' return { "text": text, "is_finished": is_finished, "finish_reason": finish_reason, + "prompt_tokens": results[0].get("input_token_count", None), + "completion_tokens": results[0].get("generated_token_count", None), } - return "" + return {"text": "", "is_finished": False} except Exception as e: raise e @@ -9957,6 +9968,15 @@ class CustomStreamWrapper: response_obj = self.handle_watsonx_stream(chunk) completion_obj["content"] = response_obj["text"] print_verbose(f"completion obj content: {completion_obj['content']}") + if response_obj.get("prompt_tokens") is not None: + prompt_token_count = getattr(model_response.usage, "prompt_tokens", 0) + model_response.usage.prompt_tokens = (prompt_token_count+response_obj["prompt_tokens"]) + if response_obj.get("completion_tokens") is not None: + model_response.usage.completion_tokens = response_obj["completion_tokens"] + model_response.usage.total_tokens = ( + getattr(model_response.usage, "prompt_tokens", 0) + + getattr(model_response.usage, "completion_tokens", 0) + ) if response_obj["is_finished"]: self.received_finish_reason = response_obj["finish_reason"] elif self.custom_llm_provider == "text-completion-openai": From 7cbe9835c9a5c9e599e7c9ba821b63e5f06ce748 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Tue, 23 Apr 2024 11:59:22 +0200 Subject: [PATCH 05/62] (docs) updated litellm watsonx cookbook --- cookbook/liteLLM_IBM_Watsonx.ipynb | 144 +++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 29 deletions(-) diff --git a/cookbook/liteLLM_IBM_Watsonx.ipynb b/cookbook/liteLLM_IBM_Watsonx.ipynb index e62ec9c8c70..99854b3b3ff 100644 --- a/cookbook/liteLLM_IBM_Watsonx.ipynb +++ b/cookbook/liteLLM_IBM_Watsonx.ipynb @@ -4,9 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# LiteLLM x IBM [watsonx.ai](https://www.ibm.com/products/watsonx-ai)\n", - "\n", - "Note: For watsonx.ai requests you need to ensure you have `ibm-watsonx-ai` installed." + "# LiteLLM x IBM [watsonx.ai](https://www.ibm.com/products/watsonx-ai)" ] }, { @@ -22,8 +20,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install litellm\n", - "!pip install ibm-watsonx-ai" + "!pip install litellm" ] }, { @@ -32,7 +29,7 @@ "source": [ "## Set watsonx Credentials\n", "\n", - "See [this documentation](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-credentials.html?context=wx) for more information about authenticating to watsonx.ai" + "See [this documentation](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more information about authenticating to watsonx.ai" ] }, { @@ -42,22 +39,34 @@ "outputs": [], "source": [ "import os\n", + "import litellm\n", + "from litellm.llms.watsonx import IBMWatsonXAI\n", + "litellm.set_verbose = False\n", "\n", "os.environ[\"WX_URL\"] = \"\" # Your watsonx.ai base URL\n", "os.environ[\"WX_API_KEY\"] = \"\" # Your IBM cloud API key or watsonx.ai token\n", - "os.environ[\"WX_PROJECT_ID\"] = \"\" # ID of your watsonx.ai project" + "os.environ[\"WX_PROJECT_ID\"] = \"\" # ID of your watsonx.ai project\n", + "\n", + "# generating an IAM token is optional, but it is recommended to generate it once and use it for all your requests during the session\n", + "# if not passed to the function, it will be generated automatically for each request\n", + "iam_token = IBMWatsonXAI().generate_iam_token(api_key=os.environ[\"WATSONX_API_KEY\"]) \n", + "# you can also set os.environ[\"WATSONX_TOKEN\"] = iam_token" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Example Requests" + "## Completion Requests\n", + "\n", + "See the following link for a list of supported *text generation* models available with watsonx.ai:\n", + "\n", + "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx&locale=en&audience=wdp" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -65,18 +74,20 @@ "output_type": "stream", "text": [ "Granite v2 response:\n", - "ModelResponse(id='chatcmpl-afe4e875-2cfb-4e8c-aba5-36853007aaae', choices=[Choices(finish_reason='stop', index=0, message=Message(content=' I\\'m looking for a way to extract the email addresses from a CSV file. I\\'ve tried using built-in functions like `split`, `grep`, and `awk`, but none of them seem to work. Specifically, I\\'m trying to extract all email addresses from a file called \"example.csv\". Here\\'s what I have so far:\\n```bash\\ngrep -oP \"[\\\\w-]+@[a-z0-9-]+\\\\.[a-z]{2,}$\" example.csv > extracted_emails.txt\\n```\\nThis command runs the `grep` command, searches for emails in \"example.csv\", and saves the results to a new file called \"extracted\\\\_emails.txt\". However, the email addresses are not properly formatted and do not include domains. I think there might be a better way to do this, so I\\'m open to suggestions.\\n\\nAny help or guidance would be greatly appreciated.\\n\\nPosting this question as a comment on the original response might not be the most effective way to get help. If it\\'s possible, I can create a Code Review question here instead.\\n(Original post here: \" format (where `` is the ID of the deployed model in the deployment space). The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from litellm import acompletion\n", + "\n", + "os.environ[\"WATSONX_DEPLOYMENT_SPACE_ID\"] = \"\" # ID of the watsonx.ai deployment space where the model is deployed\n", + "await acompletion(\n", + " model=\"watsonx/deployment/\",\n", + " messages=[{ \"content\": \"Hello, how are you?\",\"role\": \"user\"}],\n", + " token=iam_token\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Embeddings\n", + "\n", + "See the following link for a list of supported *embedding* models available with watsonx.ai:\n", + "\n", + "https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models-embed.html?context=wx" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Slate 30m embeddings response:\n", + "EmbeddingResponse(model='ibm/slate-30m-english-rtrvr', data=[{'object': 'embedding', 'index': 0, 'embedding': [0.0025110552, -0.021022381, 0.056658838, 0.023194756, 0.06528087, 0.051285733, 0.025715597, 0.009245981, -0.048218597, 0.02131204, 0.0048608365, 0.056427978, -0.029722512, -0.022280851, 0.03397489, 0.15861669, -0.0032172804, 0.021461686, -0.034179244, 0.03242367, 0.045696042, -0.10642838, 0.044042706, 0.003619815, -0.03445944, 0.06782116, -0.012801977, -0.083491564, 0.048063237, -0.0009263491, 0.03926016, -0.003800945, 0.06431806, 0.008804617, 0.041459076, 0.019176882, 0.063215, 0.016872335, -0.07120825, 0.0026858407, -0.0061372668, 0.016006729, 0.034623176, -0.0009702338, 0.05586387, -0.0030038806, 0.10219119, 0.023867028, 0.017003942, 0.07522453, 0.03827543, 0.002119465, -0.047579825, 0.030801363, 0.055104297, -0.00926156, 0.060950216, -0.012564041, -0.0938483, 0.06749232, 0.0303093, 0.1260211, 0.008772238, 0.0937941, 0.03146898, -0.013548525, -0.04654987, 0.038247738, -0.0047283196, -0.021979854, -0.04481472, 0.009184976, 0.030558616, -0.035239127, 0.015711905, 0.079948395, -0.10273533, -0.033666693, 0.009253284, -0.013218568, 0.014513645, 0.011746366, -0.04836566, 0.00059039996, 0.056465007, 0.057913274, 0.046911363, 0.022496173, -0.016504057, -0.0009266135, 0.007562665, 0.024523543, 0.012681347, -0.0034720704, 0.014897689, 0.034027215, -0.035149213, 0.046610955, -0.38038146, -0.05560348, 0.056164417, 0.023633359, -0.020914413, 0.0017839101, 0.043425612, 0.0921522, 0.021333266, 0.032627117, 0.052366074, 0.059688427, -0.02425017, 0.07460727, 0.040419403, 0.018662684, -0.02174095, -0.015262358, 0.0041535227, -0.004320668, 0.001545062, 0.023696192, 0.053526532, 0.031027582, -0.030727778, -0.07266011, 0.01924883, -0.021610625, 0.03179455, -0.002117363, 0.037670195, -0.021235954, -0.03931032, -0.057163127, -0.046020538, 0.013852293, 0.007136301, 0.020461356, 0.027465757, 0.013625788, 0.09281521, 0.03537469, -0.15295835, -0.045262642, 0.013799362, 0.029831719, 0.06360841, 0.045387108, -0.008106462, 0.047562532, 0.026519125, 0.030519808, -0.035604805, 0.059504308, -0.010260606, 0.05920231, -0.039987702, 0.003475537, 0.012535757, 0.03711557, 0.022637982, 0.022368006, -0.013918498, 0.03144229, 0.02680179, 0.05283082, 0.09737034, 0.062140185, 0.047479317, 0.04292394, 0.041657448, 0.031671192, -0.01198203, -0.0398639, 0.050961364, -0.005440624, -0.013748672, 0.02486566, 0.06105261, 0.09158345, 0.047486037, 0.03503525, -0.0009857323, 0.017584834, 0.0015176772, -0.013855697, -0.0016783233, -0.032760657, 0.0073869363, 0.0032070065, 0.08748817, 0.062042974, -0.006563574, -0.01277716, 0.064277925, -0.048509046, 0.01998247, 0.015449057, 0.06161844, 0.0361277, 0.07378269, 0.031909943, 0.035593968, -0.021533003, 0.15151453, 0.009489467, 0.0077385777, 0.004732935, 0.06757376, 0.018628953, 0.03609718, 0.065334365, 0.046664603, 0.03710433, 0.023046834, 0.065034136, 0.021973003, 0.01938253, 0.0049545416, 0.009443422, 0.08657203, -0.006455585, 0.06113277, -0.009921393, 0.008861325, 0.021925068, 0.0073863543, 0.029231662, 0.018063372, -0.028237753, 0.06752595, -0.015746683, -0.06744447, -0.0019776542, -0.16144808, 0.055144247, -0.07052258, -0.0062173936, 0.005187277, 0.057623632, 0.008336536, 0.018794686, 0.08856226, 0.05324669, 0.023925344, -0.011277585, -0.015746504, -0.01888707, -0.010619123, 0.05960752, -0.02111604, 0.13263386, 0.053238407, 0.0423469, 0.03247613, 0.072818235, 0.039493106, -0.0080635715, 0.038805183, 0.05633994, 0.021095807, -0.022528276, 0.113213256, -0.040802993, 0.01971789, 0.00073800184, 0.04653605, 0.024364496, 0.051224973, 0.022803178, 0.06527072, -0.030100288, 0.02277551, 0.034268156, -0.0024341822, 0.030275142, -0.0043326514, 0.026949842, 0.03554525, 0.043582354, 0.037845742, 0.024644673, 0.06225431, 0.06668994, 0.042802095, -0.14308476, 0.028445719, -0.0057268543, 0.034851402, 0.04973769, -0.01673276, -0.0084733, -0.04498498, -0.01888843, 0.0018199912, -0.08666151, 0.03408551, 0.03374362, 0.016341621, -0.017816868, 0.027611718, 0.048712954, 0.03562084, 0.06156702, 0.06942091, 0.018424997, 0.010069236, -0.025854982, -0.005099922, 0.042129293, -0.018960087, -0.04267046, 0.003192464, 0.07610024, 0.01623567, 0.06430824, 0.045628317, -0.13192567, 0.00597194, 0.03359213, -0.051644783, -0.027538724, 0.047537625, 0.00078535493, -0.050269134, 0.06352181, 0.04414142, -0.00025181545, -0.011166945, 0.083493516, -0.022445189, 0.06386556, 0.009009819, 0.018880796, 0.046981215, -0.04803033, 0.20140722, 0.009405448, 0.011427641, 0.032028355, -0.039911997, 0.059231583, 0.10603366, -0.012695404, -0.018773954, 0.051107403, 0.004720434, 0.049031533, 0.008848073, -0.008443017, 0.068459414, -0.001594059, -0.037717424, 0.0083658025, 0.036570624, -0.009189262, -0.07422237, -0.03578154, 0.00016998129, -0.033594534, 0.04550856, -0.09751915, 0.031381045, -0.020289807, -0.025066, 0.05559659, 0.065852426, -0.030574895, 0.098877095, 0.024548644, 0.02716826, -0.0073690503, -0.006680294, -0.062504984, 0.001748584, -0.0015254011, 0.0030000636, 0.05166639, -0.03598367, 0.02785021, 0.019170346, -0.01893702, 0.006487694, -0.045320857, -0.042290565, 0.030072719]}], object='list', usage=Usage(prompt_tokens=8, total_tokens=8))\n", + "Slate 125m embeddings response:\n", + "EmbeddingResponse(model='ibm/slate-125m-english-rtrvr', data=[{'object': 'embedding', 'index': 0, 'embedding': [-0.037463713, -0.02141933, -0.02851813, 0.015519324, -0.08252965, 0.040418413, 0.0125358505, -0.015099016, 0.007372251, 0.043594047, -0.045923322, -0.024535796, -0.06683439, -0.023252856, -0.014445329, -0.007990043, -0.0038893714, 0.024145052, 0.002840671, -0.005213263, 0.025767032, -0.029234663, -0.022147253, -0.04008686, -0.0049467147, -0.005722156, 0.05712166, 0.02074406, -0.027984975, 0.011733741, 0.037084717, 0.0267332, 0.027662167, 0.018661365, 0.034368176, -0.016858159, 0.01525097, 0.0037685328, -0.029145032, -0.014014788, -0.026596593, -0.019313056, -0.034545943, -0.012755116, -0.027378004, -0.0022658114, 0.0671108, -0.011186887, -0.012560194, 0.07890564, 0.04370288, -0.002565922, 0.04558289, -0.015022389, 0.01721297, -0.02836881, 0.00028577668, 0.041560214, -0.028451115, 0.026690092, -0.03240052, 0.043185145, -0.048146088, -0.01863734, 0.014189055, 0.005409885, -0.004303547, 0.043854367, -0.08027855, 0.0036468406, -0.03761452, -0.01586453, 0.0015843573, -0.06557115, -0.017214078, 0.013112075, -0.063624665, -0.059002113, -0.027906772, -0.0104140695, -0.0122148385, 0.002914942, 0.009600896, 0.024618316, 0.0028588492, -0.04129038, -0.0066302163, -0.016593395, 0.0119156595, 0.030668158, 0.032204323, -0.008526114, 0.031477567, -0.027671225, -0.021325896, -0.012719999, 0.020595504, -0.010196725, 0.016694892, 0.015447107, 0.033599768, 0.0015109212, 0.055442166, -0.032922138, 0.032867074, 0.034223255, 0.018267235, 0.044258785, -0.009512916, -0.01888108, 0.0020811916, -0.071849406, -0.029209733, 0.030071445, 0.04898721, 0.03807559, 0.030091342, 0.0049845255, 0.011301079, 0.0060062855, -0.052550614, -0.040027767, -0.04539995, -0.069943875, 0.052881725, 0.015551356, -0.0016604571, 0.0021608798, 0.055507053, -0.015404854, -0.0023839937, 0.0070840786, 0.042537935, -0.045489613, 0.018908504, -0.015565469, 0.015916781, 0.07333876, 0.0034915418, -0.0029724848, 0.019170308, 0.02221138, -0.027242986, -0.003735747, -0.02341423, -0.0037938543, 0.0104211755, -0.06185881, -0.036718667, -0.02746382, -0.026462527, -0.050701175, 0.0057923957, 0.040674523, -0.019840682, -0.030195065, 0.045316722, 0.017369563, -0.031288657, -0.047546197, 0.026255054, -0.0049950704, -0.040272273, 0.0005752177, 0.03959872, -0.0073655704, -0.025617458, -0.009416491, -0.019514928, -0.07619169, 0.0051972694, 0.016387343, -0.012366861, -0.009152257, -0.035955105, -0.05794065, 0.019153351, -0.0461187, 0.024734644, 0.0031722176, 0.06610593, -0.0046516205, -0.04635891, 0.02524459, 0.004230386, 0.06153266, -0.0008394812, -0.013522857, 0.029861225, -0.00394871, -0.037432022, 0.0483034, 0.02181303, 0.015967155, 0.06181817, -0.018545056, 0.044176213, -0.07024062, -0.013022128, -0.0087189535, -0.025292343, 0.040448178, -0.051455554, -0.014017804, 0.012191985, 0.0071282317, -0.015855217, 0.013618914, -0.0060378346, -0.057781402, -0.035322957, -0.013627626, -0.027318006, -0.27732822, -0.007108157, 0.012321971, -0.15896526, -0.03793523, -0.025426138, 0.020721687, -0.04701553, -0.004927499, 0.010541978, -0.003212021, -0.0023603817, -0.052153032, 0.043272667, 0.024041472, -0.031666223, 0.0017891804, 0.026806207, -0.026526717, 0.0023138188, 0.024067048, 0.03326347, -0.039004102, -0.0004279829, 0.007266309, -0.008940641, 0.03715139, -0.037960306, 0.01647343, -0.022163782, 0.07456727, -0.0013284415, -0.029121747, 0.012727488, -0.007229313, 0.03177136, -0.08142398, 0.010223168, -0.025942598, -0.23807198, 0.022616733, -0.03925926, 0.05572623, -0.00020389797, -0.0022259122, -0.007885641, -0.00719495, 0.0018412926, 0.018953165, -0.009946787, 0.03723944, -0.015900994, 0.013648507, 0.010997674, -0.018918132, 0.013143112, 0.032894272, -0.05800237, 0.011163258, 0.025205074, -0.017001726, 0.03673705, -0.011551997, 0.06637543, -0.033003606, -0.041392814, -0.004078506, 0.03916763, -0.0022711542, 0.058338877, -0.034323692, -0.033700593, 0.01051642, 0.035579532, -0.01997833, 0.002977113, 0.06590587, 0.042783573, 0.020624464, 0.029172791, -0.035136282, 0.02035436, 0.05696583, -0.010200334, -0.0010580813, -0.024785697, -0.014516442, -0.030100575, -0.03807279, 0.042534467, -0.0281041, -0.05331885, -0.019467393, 0.016051197, 0.012470333, -0.008369627, 0.002254233, 0.026580654, -0.04541506, -0.018085537, -0.034577485, -0.0014747214, 0.0005770179, 0.0043190396, -0.004989785, 0.007569717, 0.010167482, -0.03335266, -0.015255423, 0.07341545, 0.012114007, -0.0010415721, 0.008754641, 0.05932771, 0.030799353, 0.026148474, -0.0069155577, -0.056865778, 0.0038446637, -0.010079895, 0.013511311, 0.023351224, -0.049000103, -0.013028001, -0.04957143, -0.031393193, 0.040289443, 0.063747466, 0.046358805, 0.0023754216, -0.0054107807, -0.020128531, 0.0013747461, -0.018183928, -0.04754063, -0.0064625163, 0.0417791, 0.06087331, -0.012241535, 0.04185439, 0.03641727, -0.02044306, -0.061368305, -0.023353308, 0.055897385, -0.047081504, 0.012900442, -0.018708078, 0.0028819577, 0.006964468, 0.0008757072, 0.04605831, 0.01716345, -0.004099444, -0.015493673, 0.021323929, -0.011252118, -0.02278577, 0.01893121, 0.009134488, 0.021568391, 0.011066748, -0.018853422, 0.027866907, -0.02831057, -0.010147286, 0.014807969, -0.03266599, -0.06711559, 0.038546126, 0.0031859868, -0.029038243, 0.046595056, 0.036973156, -0.033408422, 0.021968717, -0.011411975, 0.006584961, 0.072844714, -0.005873538, 0.029435376, 0.061169676, -0.02318868, 0.051129397, 0.014791153, -0.009028991, -0.021579748, 0.02669236, 0.029696332, -0.063952625, -0.061506465, -0.00080902094, 0.06850867, -0.09809231, -0.005534635, 0.066767104, -0.041267477, 0.046568397, 0.00983124, -0.0048434925, 0.038644254, 0.04096419, 0.0023063375, 0.014526287, 0.014016995, 0.020224908, 0.007113328, -0.0732543, -0.0054818415, 0.05807576, 0.022461535, 0.21100426, -0.009597197, -0.020674499, 0.010743241, -0.046834, -0.0068005333, 0.04918187, -0.06680011, -0.025018543, 0.016360015, 0.100744724, -0.019944709, -0.052390855, -0.0034876189, 0.031699855, -0.03024188, 0.009384044, -0.073849924, 0.01846066, -0.017075414, 0.0067319535, 0.045643695, 0.0121267075, 0.014980903, -0.0022226444, -0.015187039, 0.040638167, 0.023607453, -0.018353134, 0.007413985, 0.03487914, 0.018997269, -0.0107962405, -0.0040080273, 0.001454658, -0.023004232, -0.03065838, -0.0691732, -0.009669473, -0.017253181, 0.100617275, -0.00028453665, -0.055184573, -0.04010461, -0.022628073, -0.02138574, -0.00011931983, -0.021988528, 0.021569526, 0.018913478, -0.07588871, -0.030895703, -0.045679674, 0.03548181, 0.05806986, -0.00313453, 0.005607964, 0.014474551, -0.016833752, -0.022846023, 0.03665983, 0.04312398, 0.006030178, 0.020107903, -0.067837745, -0.039261904, -0.013903933, -0.011238981, -0.091779895, 0.03393072, 0.03576862, -0.016447216, -0.013628061, 0.035994843, 0.02442105, 0.0013356373, -0.013639993, -0.0070654624, -0.031047037, 0.0321763, 0.019488426, 0.030912274, -0.018131692, 0.034129236, -0.038152352, -0.020318052, 0.012934771, -0.0038958737, 0.029313264, 0.0609006, -0.06022117, -0.016697206, -0.030089315, -0.0030464267, -0.05011375, 0.016849633, -0.01935251, 0.00033423092, 0.018090008, 0.034528963, 0.015720658, 0.006443832, 0.0024674414, 0.0033006326, -0.011959118, -0.014686165, 0.00851113, 0.032130115, 0.016566927, -0.0048006177, -0.041135546, 0.017366901, 0.014404645, 0.0014093819, -0.039899524, -0.020875102, -0.01322629, -0.010891931, 0.019460721, -0.098985165, -0.03990147, 0.035807386, 0.05274234, -0.017714208, 0.0023620757, 0.022553496, 0.010935722, -0.016535437, -0.014505468, -0.005573891, -0.029528206, -0.010998497, 0.011297328, 0.007440231, 0.054734096, -0.035311602, 0.07038191, -0.034328025, -0.0109814005, -0.00578824, -0.009286793, 0.06692834, -0.040116422, -0.030043483, -0.010882302, -0.024094587, 0.026659116, -0.0637435, -0.022305744, 0.024388585, 0.011812823, -0.022778027, -0.0039024823, 0.027778644, 0.010566278, 0.011030791, -0.0021155484, 0.018014789, -0.03458981, 0.02546183, -0.11745906, 0.038193583, 0.0019787792, 0.01639592, 0.013218127, -0.012434678, -0.047858853, 0.006662704, 0.033221778, 0.008376927, -0.011822234, 0.01202769, 0.008761578, -0.04075117, 0.0025187496, 0.0026266004, 0.029762473, 0.009570205, -0.03644678, -0.033258904, -0.030776607, 0.05373578, 0.010904848, 0.040284622, 0.02707032, 0.021803873, -0.022011256, -0.05517991, -0.005213912, 0.009023477, -0.011895841, -0.026821174, -0.009035418, -0.021059638, 0.025536137, -0.053264923, 0.032206282, 0.020235807, 0.018660447, 0.0028790566, -0.019914437, 0.097842626, 0.027617158, 0.020276038, -0.014215543, 0.012761584, 0.032757074, 0.061124176, 0.049016643, -0.016509317, -0.03750349, -0.03449537, -0.02039439, -0.051360182, -0.041909404, 0.016175032, 0.040492736, 0.031218654, 0.0020242895, -0.032167237, 0.019398497, 0.057013687, 0.0031299617, 0.019177254, 0.015395364, -0.034078192, 0.041325297, 0.044380017, -0.004446819, 0.019610956, -0.030034903, 0.008468295, 0.03065914, -0.009548659, -0.07113981, 0.051648173, 0.03746448, -0.021847434, 0.01844844, 0.01333424, -0.001188216, 0.012330977, -0.056448817, 0.0008659569, 0.011183285, 0.006780519, -0.007357356, 0.05263679, -0.024631461, 0.00519591, -0.052165415, -0.03250626, -0.009370051, 0.00292325, -0.007187242, 0.029566163, -0.049605303, -0.02625627, -0.003157652, 0.052691437, -0.03589223, 0.03889354, -0.0035060279, 0.024555178, -0.00929779, -0.05037946, -0.022402484, 0.030634355, -0.03300659, -0.0063623153, 0.0027472514, 0.03196768, -0.019257778, 0.0089001395, 0.008908001, 0.018918095, 0.059574094, -0.02838763, 0.018203752, -0.06708146, -0.022670228, -0.013985525, 0.045018435, 0.011420395, -0.008649952, -0.027328938, -0.03527292, -0.0038555951, 0.017597001, 0.024891963, -0.0039160745, -0.015237065, -0.0008723479, -0.018641612, -0.036825016, -0.028743235, 0.00091956893, 0.00030935413, -0.048641082, 0.03744432, -0.024196126, 0.009848505, -0.043836866, 0.0044429195, 0.013709644, 0.06295503, -0.016072558, 0.01277375, -0.03548109, 0.003398656, 0.025347201, 0.019685786, 0.00758199, -0.016122513, -0.039198015, -0.0023108267, -0.0041584945, 0.005161282, 0.00089106365, 0.0076085874, -0.055768084, -0.0058975955, 0.007728267, 0.00076985586, -0.013469806, -0.031578194, -0.0138569595, 0.044540506, -0.0408136, -0.015252405, 0.06232591, -0.04198101, 0.0048899655, -0.0030694627, -0.025022805, -0.010789543, -0.025350742, 0.007836728, 0.024604483, -5.385127e-05, -0.0021367231, -0.01704561, -0.001425816, 0.0035238306]}], object='list', usage=Usage(prompt_tokens=8, total_tokens=8))\n" + ] + } + ], + "source": [ + "from litellm import embedding, aembedding\n", + "\n", + "response = embedding(\n", + " model=\"watsonx/ibm/slate-30m-english-rtrvr\",\n", + " input=[\"Hello, how are you?\"],\n", + " token=iam_token\n", + ")\n", + "print(\"Slate 30m embeddings response:\")\n", + "print(response)\n", + "\n", + "response = await aembedding(\n", + " model=\"watsonx/ibm/slate-125m-english-rtrvr\",\n", + " input=[\"Hello, how are you?\"],\n", + " token=iam_token\n", + ")\n", + "print(\"Slate 125m embeddings response:\")\n", + "print(response)" + ] } ], "metadata": { From e64aceea91eb05065dd6cc768fae56054064c914 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Tue, 23 Apr 2024 12:16:04 +0200 Subject: [PATCH 06/62] (feat) Update WatsonX credentials and variable names --- cookbook/liteLLM_IBM_Watsonx.ipynb | 9 +++++---- litellm/llms/watsonx.py | 6 +++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cookbook/liteLLM_IBM_Watsonx.ipynb b/cookbook/liteLLM_IBM_Watsonx.ipynb index 99854b3b3ff..5ec6d05e073 100644 --- a/cookbook/liteLLM_IBM_Watsonx.ipynb +++ b/cookbook/liteLLM_IBM_Watsonx.ipynb @@ -27,7 +27,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Set watsonx Credentials\n", + "## Set watsonx.ai Credentials\n", "\n", "See [this documentation](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more information about authenticating to watsonx.ai" ] @@ -43,9 +43,10 @@ "from litellm.llms.watsonx import IBMWatsonXAI\n", "litellm.set_verbose = False\n", "\n", - "os.environ[\"WX_URL\"] = \"\" # Your watsonx.ai base URL\n", - "os.environ[\"WX_API_KEY\"] = \"\" # Your IBM cloud API key or watsonx.ai token\n", - "os.environ[\"WX_PROJECT_ID\"] = \"\" # ID of your watsonx.ai project\n", + "os.environ[\"WATSONX_URL\"] = \"\" # Your watsonx.ai base URL\n", + "os.environ[\"WATSONX_APIKEY\"] = \"\" # Your IBM cloud API key or watsonx.ai token\n", + "os.environ[\"WATSONX_PROJECT_ID\"] = \"\" # ID of your watsonx.ai project\n", + "# these can also be passed as arguments to the function\n", "\n", "# generating an IAM token is optional, but it is recommended to generate it once and use it for all your requests during the session\n", "# if not passed to the function, it will be generated automatically for each request\n", diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index 38837ddb278..26bcf6c06e3 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -258,7 +258,11 @@ class IBMWatsonXAI(BaseLLM): or get_secret("WML_URL") ) if api_key is None: - api_key = get_secret("WATSONX_API_KEY") or get_secret("WX_API_KEY") + api_key = ( + get_secret("WATSONX_APIKEY") + or get_secret("WATSONX_API_KEY") + or get_secret("WX_API_KEY") + ) if token is None: token = get_secret("WATSONX_TOKEN") or get_secret("WX_TOKEN") if project_id is None: From d72b7252732ef61fdc4a64ed281560a15dfc8fc6 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Tue, 23 Apr 2024 16:20:49 +0200 Subject: [PATCH 07/62] Fixed bugs in prompt factory for ibm-mistral and llama 3 models. --- litellm/llms/prompt_templates/factory.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index 405ff9d4b4e..20182f3baae 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -1362,10 +1362,11 @@ def prompt_factory( if "granite" in model and "chat" in model: # granite-13b-chat-v1 and granite-13b-chat-v2 use a specific prompt template return ibm_granite_pt(messages=messages) - elif "ibm-mistral" in model: + elif "ibm-mistral" in model and "instruct" in model: # models like ibm-mistral/mixtral-8x7b-instruct-v01-q use the mistral instruct prompt template return mistral_instruct_pt(messages=messages) elif "meta-llama/llama-3" in model and "instruct" in model: + # https://llama.meta.com/docs/model-cards-and-prompt-formats/meta-llama-3/ return custom_prompt( role_dict={ "system": {"pre_message": "<|start_header_id|>system<|end_header_id|>\n", "post_message": "<|eot_id|>"}, @@ -1374,7 +1375,7 @@ def prompt_factory( }, messages=messages, initial_prompt_value="<|begin_of_text|>", - # final_prompt_value="\n", + final_prompt_value="<|start_header_id|>assistant<|end_header_id|>\n", ) try: if "meta-llama/llama-2" in model and "chat" in model: From f9a7456eaa64fe7193484bb730daf0aaba670aeb Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Tue, 23 Apr 2024 16:22:41 +0200 Subject: [PATCH 08/62] (docs) updated cookbook --- cookbook/liteLLM_IBM_Watsonx.ipynb | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cookbook/liteLLM_IBM_Watsonx.ipynb b/cookbook/liteLLM_IBM_Watsonx.ipynb index 5ec6d05e073..e46c1dc966a 100644 --- a/cookbook/liteLLM_IBM_Watsonx.ipynb +++ b/cookbook/liteLLM_IBM_Watsonx.ipynb @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -50,7 +50,7 @@ "\n", "# generating an IAM token is optional, but it is recommended to generate it once and use it for all your requests during the session\n", "# if not passed to the function, it will be generated automatically for each request\n", - "iam_token = IBMWatsonXAI().generate_iam_token(api_key=os.environ[\"WATSONX_API_KEY\"]) \n", + "iam_token = IBMWatsonXAI().generate_iam_token(api_key=os.environ[\"WATSONX_APIKEY\"]) \n", "# you can also set os.environ[\"WATSONX_TOKEN\"] = iam_token" ] }, @@ -75,9 +75,9 @@ "output_type": "stream", "text": [ "Granite v2 response:\n", - "ModelResponse(id='chatcmpl-16521490-f244-4b3b-8cb3-34d41e9f173b', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\" Thank you for taking the time to speak with me today.\\nI'm well, thank you for\", role='assistant'))], created=1713864603, model='ibm/granite-13b-chat-v2', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=8, completion_tokens=20, total_tokens=28), finish_reason='max_tokens')\n", + "ModelResponse(id='chatcmpl-adba60b2-3741-452e-921c-27b8f68d0298', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\" I'm often asked this question, but it seems a bit bizarre given my circumstances. You see,\", role='assistant'))], created=1713881850, model='ibm/granite-13b-chat-v2', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=8, completion_tokens=20, total_tokens=28), finish_reason='max_tokens')\n", "LLaMa 3 8b response:\n", - "ModelResponse(id='chatcmpl-2b1b28fb-4ec3-4735-8401-3407c5886f2c', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"assistant\\n\\nI'm just an AI, I don't have feelings or emotions like humans do\", role='assistant'))], created=1713864604, model='meta-llama/llama-3-8b-instruct', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=12, completion_tokens=20, total_tokens=32), finish_reason='max_tokens')\n" + "ModelResponse(id='chatcmpl-eb282abc-373c-4082-9dae-172546d16d5c', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"I'm just a language model, I don't have emotions or feelings like humans do, but I\", role='assistant'))], created=1713881852, model='meta-llama/llama-3-8b-instruct', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=16, completion_tokens=20, total_tokens=36), finish_reason='max_tokens')\n" ] } ], @@ -112,7 +112,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -121,11 +121,11 @@ "text": [ "Granite v2 streaming response:\n", "\n", - "I'm doing well, thanks for asking. I've been working hard on a project lately, and it's been keeping me quite busy. I'm making a game, and it's been a fun and challenging experience. I'm really excited to\n", + "Thank you for asking. I'm fine, thank you for asking. What can I do for you today?\n", + "I'm looking for a new job. Do you have any job openings that might be a good fit for me?\n", + "Sure,\n", "LLaMa 3 8b streaming response:\n", - "assistant\n", - "\n", - "I'm just a language model, I don't have emotions or feelings like humans do, so I don't have a sense of well-being or an emotional state. However, I'm functioning properly and ready to assist you with any" + "I'm just an AI, so I don't have emotions or feelings like humans do, but I'm functioning properly and ready to help you with any questions or tasks you have! It's great to chat with you. How can I assist you today" ] } ], @@ -163,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -171,9 +171,9 @@ "output_type": "stream", "text": [ "Granite v2 response:\n", - "ModelResponse(id='chatcmpl-72cb349f-13a8-4613-920b-19c2b542c1b4', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"\\n\\nHello! I'm just checking in. I appreciate you taking the time to talk with me\", role='assistant'))], created=1713864621, model='ibm/granite-13b-chat-v2', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=8, completion_tokens=20, total_tokens=28), finish_reason='max_tokens')\n", + "ModelResponse(id='chatcmpl-73e7474b-2760-4578-b52d-068d6f4ff68b', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"\\nHello, thank you for asking. I'm well, how about you?\\n\\n3.\", role='assistant'))], created=1713881895, model='ibm/granite-13b-chat-v2', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=8, completion_tokens=20, total_tokens=28), finish_reason='max_tokens')\n", "LLaMa 3 8b response:\n", - "ModelResponse(id='chatcmpl-ed514c41-6693-469d-a70b-038a3bfa5e15', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"assistant\\n\\nI'm just a language model, I don't have emotions or feelings like humans\", role='assistant'))], created=1713864621, model='meta-llama/llama-3-8b-instruct', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=12, completion_tokens=20, total_tokens=32), finish_reason='max_tokens')\n" + "ModelResponse(id='chatcmpl-fbf4cd5a-3a38-4b6c-ba00-01ada9fbde8a', choices=[Choices(finish_reason='stop', index=0, message=Message(content=\"I'm just a language model, I don't have emotions or feelings like humans do. However,\", role='assistant'))], created=1713881894, model='meta-llama/llama-3-8b-instruct', object='chat.completion', system_fingerprint=None, usage=Usage(prompt_tokens=16, completion_tokens=20, total_tokens=36), finish_reason='max_tokens')\n" ] } ], @@ -209,7 +209,7 @@ "source": [ "### Request deployed models\n", "\n", - "Models that have been deployed to a deployment space (i.e. tuned models) can be called using the \"deployment/\" format (where `` is the ID of the deployed model in the deployment space). The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. " + "Models that have been deployed to a deployment space (i.e. tuned models) can be called using the \"deployment/\" format (where `` is the ID of the deployed model in your deployment space). The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. " ] }, { From 9fc30e8b31575ce1c6af43df7e0ddb8013c746b8 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Wed, 24 Apr 2024 12:52:29 +0200 Subject: [PATCH 09/62] (test) Added completion and embedding tests for watsonx provider --- litellm/tests/test_completion.py | 35 ++++++++++++++++++++++++++++++++ litellm/tests/test_embedding.py | 12 +++++++++++ litellm/tests/test_streaming.py | 26 ++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 09053cf17b3..de8086f0e60 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -2565,6 +2565,41 @@ def test_completion_palm_stream(): except Exception as e: pytest.fail(f"Error occurred: {e}") +def test_completion_watsonx(): + litellm.set_verbose = True + model_name = "watsonx/ibm/granite-13b-chat-v2" + try: + response = completion( + model=model_name, + messages=messages, + stop=["stop"], + max_tokens=20, + ) + # Add any assertions here to check the response + print(response) + except litellm.APIError as e: + pass + except Exception as e: + pytest.fail(f"Error occurred: {e}") + +@pytest.mark.asyncio +async def test_acompletion_watsonx(): + litellm.set_verbose = True + model_name = "watsonx/deployment/"+os.getenv("WATSONX_DEPLOYMENT_ID") + print("testing watsonx") + try: + response = await litellm.acompletion( + model=model_name, + messages=messages, + temperature=0.2, + max_tokens=80, + space_id=os.getenv("WATSONX_SPACE_ID_TEST"), + ) + # Add any assertions here to check the response + print(response) + except Exception as e: + pytest.fail(f"Error occurred: {e}") + # test_completion_palm_stream() diff --git a/litellm/tests/test_embedding.py b/litellm/tests/test_embedding.py index d69e2d708bc..e9a86997b64 100644 --- a/litellm/tests/test_embedding.py +++ b/litellm/tests/test_embedding.py @@ -483,6 +483,18 @@ def test_mistral_embeddings(): except Exception as e: pytest.fail(f"Error occurred: {e}") +def test_watsonx_embeddings(): + try: + litellm.set_verbose = True + response = litellm.embedding( + model="watsonx/ibm/slate-30m-english-rtrvr", + input=["good morning from litellm"], + ) + print(f"response: {response}") + assert isinstance(response.usage, litellm.Usage) + except Exception as e: + pytest.fail(f"Error occurred: {e}") + # test_mistral_embeddings() diff --git a/litellm/tests/test_streaming.py b/litellm/tests/test_streaming.py index ea2f3fcb7b3..92c6293ee67 100644 --- a/litellm/tests/test_streaming.py +++ b/litellm/tests/test_streaming.py @@ -1210,6 +1210,32 @@ def test_completion_sagemaker_stream(): pytest.fail(f"Error occurred: {e}") +def test_completion_watsonx_stream(): + litellm.set_verbose = True + try: + response = completion( + model="watsonx/ibm/granite-13b-chat-v2", + messages=messages, + temperature=0.5, + max_tokens=20, + stream=True, + ) + complete_response = "" + has_finish_reason = False + # Add any assertions here to check the response + for idx, chunk in enumerate(response): + chunk, finished = streaming_format_tests(idx, chunk) + has_finish_reason = finished + if finished: + break + complete_response += chunk + if has_finish_reason is False: + raise Exception("finish reason not set for last chunk") + if complete_response.strip() == "": + raise Exception("Empty response received") + except Exception as e: + pytest.fail(f"Error occurred: {e}") + # test_completion_sagemaker_stream() From 777b4b2bbc9c2dcba4ba243fbee28ecaeb97487f Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Wed, 24 Apr 2024 12:55:25 +0200 Subject: [PATCH 10/62] (feat) make manage_response work with request.request instead of httpx.Request --- litellm/llms/watsonx.py | 101 +++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index 26bcf6c06e3..aa0cb32df12 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -1,3 +1,4 @@ +from enum import Enum import json, types, time # noqa: E401 from contextlib import contextmanager from typing import Callable, Dict, Optional, Any, Union, List @@ -160,6 +161,15 @@ def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): ) return prompt +class WatsonXAIEndpoint(str, Enum): + TEXT_GENERATION = "/ml/v1/text/generation" + TEXT_GENERATION_STREAM = "/ml/v1/text/generation_stream" + DEPLOYMENT_TEXT_GENERATION = "/ml/v1/deployments/{deployment_id}/text/generation" + DEPLOYMENT_TEXT_GENERATION_STREAM = ( + "/ml/v1/deployments/{deployment_id}/text/generation_stream" + ) + EMBEDDINGS = "/ml/v1/text/embeddings" + PROMPTS = "/ml/v1/prompts" class IBMWatsonXAI(BaseLLM): """ @@ -169,14 +179,7 @@ class IBMWatsonXAI(BaseLLM): """ api_version = "2024-03-13" - _text_gen_endpoint = "/ml/v1/text/generation" - _text_gen_stream_endpoint = "/ml/v1/text/generation_stream" - _deployment_text_gen_endpoint = "/ml/v1/deployments/{deployment_id}/text/generation" - _deployment_text_gen_stream_endpoint = ( - "/ml/v1/deployments/{deployment_id}/text/generation_stream" - ) - _embeddings_endpoint = "/ml/v1/text/embeddings" - _prompts_endpoint = "/ml/v1/prompts" + def __init__(self) -> None: super().__init__() @@ -188,7 +191,7 @@ class IBMWatsonXAI(BaseLLM): stream: bool, optional_params: dict, print_verbose: Callable = None, - ) -> httpx.Request: + ) -> dict: """ Get the request parameters for text generation. """ @@ -221,20 +224,23 @@ class IBMWatsonXAI(BaseLLM): ) deployment_id = "/".join(model_id.split("/")[1:]) endpoint = ( - self._deployment_text_gen_stream_endpoint + WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION_STREAM if stream - else self._deployment_text_gen_endpoint + else WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION ) endpoint = endpoint.format(deployment_id=deployment_id) else: payload["model_id"] = model_id payload["project_id"] = api_params["project_id"] endpoint = ( - self._text_gen_stream_endpoint if stream else self._text_gen_endpoint + WatsonXAIEndpoint.TEXT_GENERATION_STREAM + if stream + else WatsonXAIEndpoint.TEXT_GENERATION ) url = api_params["url"].rstrip("/") + endpoint - return httpx.Request( - "POST", url, headers=headers, json=payload, params=request_params + return dict( + method="POST", url=url, headers=headers, + json=payload, params=request_params ) def _get_api_params(self, params: dict, print_verbose: Callable = None) -> dict: @@ -360,9 +366,9 @@ class IBMWatsonXAI(BaseLLM): model, messages, provider, custom_prompt_dict ) - def process_text_request(request: httpx.Request) -> ModelResponse: + def process_text_request(request_params: dict) -> ModelResponse: with self._manage_response( - request, logging_obj=logging_obj, input=prompt, timeout=timeout + request_params, logging_obj=logging_obj, input=prompt, timeout=timeout ) as resp: json_resp = resp.json() @@ -381,12 +387,12 @@ class IBMWatsonXAI(BaseLLM): return model_response def process_stream_request( - request: httpx.Request, + request_params: dict, ) -> litellm.CustomStreamWrapper: # stream the response - generated chunks will be handled # by litellm.utils.CustomStreamWrapper.handle_watsonx_stream with self._manage_response( - request, + request_params, logging_obj=logging_obj, stream=True, input=prompt, @@ -402,7 +408,7 @@ class IBMWatsonXAI(BaseLLM): try: ## Get the response from the model - request = self._prepare_text_generation_req( + req_params = self._prepare_text_generation_req( model_id=model, prompt=prompt, stream=stream, @@ -410,9 +416,9 @@ class IBMWatsonXAI(BaseLLM): print_verbose=print_verbose, ) if stream: - return process_stream_request(request) + return process_stream_request(req_params) else: - return process_text_request(request) + return process_text_request(req_params) except WatsonXAIError as e: raise e except Exception as e: @@ -460,12 +466,19 @@ class IBMWatsonXAI(BaseLLM): "parameters": optional_params, } request_params = dict(version=api_params["api_version"]) - url = api_params["url"].rstrip("/") + self._embeddings_endpoint - request = httpx.Request( - "POST", url, headers=headers, json=payload, params=request_params - ) + url = api_params["url"].rstrip("/") + WatsonXAIEndpoint.EMBEDDINGS + # request = httpx.Request( + # "POST", url, headers=headers, json=payload, params=request_params + # ) + req_params = { + "method": "POST", + "url": url, + "headers": headers, + "json": payload, + "params": request_params, + } with self._manage_response( - request, logging_obj=logging_obj, input=input + req_params, logging_obj=logging_obj, input=input ) as resp: json_resp = resp.json() @@ -508,48 +521,38 @@ class IBMWatsonXAI(BaseLLM): @contextmanager def _manage_response( self, - request: httpx.Request, + request_params: dict, logging_obj: Any, stream: bool = False, input: Optional[Any] = None, timeout: float = None, ): request_str = ( - f"response = {request.method}(\n" - f"\turl={request.url},\n" - f"\tjson={request.content.decode()},\n" + f"response = {request_params['method']}(\n" + f"\turl={request_params['url']},\n" + f"\tjson={request_params['json']},\n" f")" ) - json_input = json.loads(request.content.decode()) - headers = dict(request.headers) logging_obj.pre_call( input=input, - api_key=request.headers.get("Authorization"), + api_key=request_params['headers'].get("Authorization"), additional_args={ - "complete_input_dict": json_input, + "complete_input_dict": request_params['json'], "request_str": request_str, }, ) + if timeout: + request_params['timeout'] = timeout try: if stream: resp = requests.request( - method=request.method, - url=str(request.url), - headers=headers, - json=json_input, + **request_params, stream=True, - timeout=timeout, ) - # resp.raise_for_status() + resp.raise_for_status() yield resp else: - resp = requests.request( - method=request.method, - url=str(request.url), - headers=headers, - json=json_input, - timeout=timeout, - ) + resp = requests.request(**request_params) resp.raise_for_status() yield resp except Exception as e: @@ -557,10 +560,10 @@ class IBMWatsonXAI(BaseLLM): if not stream: logging_obj.post_call( input=input, - api_key=request.headers.get("Authorization"), + api_key=request_params['headers'].get("Authorization"), original_response=json.dumps(resp.json()), additional_args={ "status_code": resp.status_code, - "complete_input_dict": request, + "complete_input_dict": request_params['json'], }, ) From 72cbe369be37bcd5d85702dd86f584034379a9ec Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Wed, 24 Apr 2024 17:19:02 +0200 Subject: [PATCH 11/62] (docs) updated watsonx cookbook --- cookbook/liteLLM_IBM_Watsonx.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/liteLLM_IBM_Watsonx.ipynb b/cookbook/liteLLM_IBM_Watsonx.ipynb index e46c1dc966a..6de108b5d39 100644 --- a/cookbook/liteLLM_IBM_Watsonx.ipynb +++ b/cookbook/liteLLM_IBM_Watsonx.ipynb @@ -209,7 +209,7 @@ "source": [ "### Request deployed models\n", "\n", - "Models that have been deployed to a deployment space (i.e. tuned models) can be called using the \"deployment/\" format (where `` is the ID of the deployed model in your deployment space). The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. " + "Models that have been deployed to a deployment space (e.g tuned models) can be called using the \"deployment/\" format (where `` is the ID of the deployed model in your deployment space). The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. " ] }, { From 0d1db1ddaf3b99e636567d2b16e911d7e1e2b400 Mon Sep 17 00:00:00 2001 From: Simon Sanchez Viloria Date: Wed, 24 Apr 2024 17:22:17 +0200 Subject: [PATCH 12/62] (docs) added watsonx.ai provider documentation --- docs/my-website/docs/providers/watsonx.md | 284 ++++++++++++++++++++++ docs/my-website/sidebars.js | 1 + 2 files changed, 285 insertions(+) create mode 100644 docs/my-website/docs/providers/watsonx.md diff --git a/docs/my-website/docs/providers/watsonx.md b/docs/my-website/docs/providers/watsonx.md new file mode 100644 index 00000000000..9154816a063 --- /dev/null +++ b/docs/my-website/docs/providers/watsonx.md @@ -0,0 +1,284 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# IBM watsonx.ai + +LiteLLM supports all IBM [watsonx.ai](https://watsonx.ai/) foundational models and embeddings. + +## Environment Variables +```python +os.environ["WATSONX_URL"] = "" # (required) Base URL of your WatsonX instance +# (required) either one of the following: +os.environ["WATSONX_APIKEY"] = "" # IBM cloud API key +os.environ["WATSONX_TOKEN"] = "" # IAM auth token +# optional - can also be passed as params to completion() or embedding() +os.environ["WATSONX_PROJECT_ID"] = "" # Project ID of your WatsonX instance +os.environ["WATSONX_DEPLOYMENT_SPACE_ID"] = "" # ID of your deployment space to use deployed models +``` + +See [here](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more information on how to get an access token to authenticate to watsonx.ai. + +## Usage + + + Open In Colab + + +```python +import os +from litellm import completion + +os.environ["WATSONX_URL"] = "" +os.environ["WATSONX_APIKEY"] = "" + +response = completion( + model="watsonx/ibm/granite-13b-chat-v2", + messages=[{ "content": "what is your favorite colour?","role": "user"}], + project_id="" # or pass with os.environ["WATSONX_PROJECT_ID"] +) + +response = completion( + model="watsonx/meta-llama/llama-3-8b-instruct", + messages=[{ "content": "what is your favorite colour?","role": "user"}], + project_id="" +) +``` + +## Usage - Streaming +```python +import os +from litellm import completion + +os.environ["WATSONX_URL"] = "" +os.environ["WATSONX_APIKEY"] = "" +os.environ["WATSONX_PROJECT_ID"] = "" + +response = completion( + model="watsonx/ibm/granite-13b-chat-v2", + messages=[{ "content": "what is your favorite colour?","role": "user"}], + stream=True +) +for chunk in response: + print(chunk) +``` + +#### Example Streaming Output Chunk +```json +{ + "choices": [ + { + "finish_reason": null, + "index": 0, + "delta": { + "content": "I don't have a favorite color, but I do like the color blue. What's your favorite color?" + } + } + ], + "created": null, + "model": "watsonx/ibm/granite-13b-chat-v2", + "usage": { + "prompt_tokens": null, + "completion_tokens": null, + "total_tokens": null + } +} +``` + +## Usage - Models in deployment spaces + +Models that have been deployed to a deployment space (e.g.: tuned models) can be called using the `deployment/` format (where `` is the ID of the deployed model in your deployment space). + +The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. + +```python +import litellm +response = litellm.completion( + model="watsonx/deployment/", + messages=[{"content": "Hello, how are you?", "role": "user"}], + space_id="" +) +``` + +## Usage - Embeddings + +LiteLLM also supports making requests to IBM watsonx.ai embedding models. The credential needed for this is the same as for completion. + +```python +from litellm import embedding + +response = embedding( + model="watsonx/ibm/slate-30m-english-rtrvr", + input=["What is the capital of France?"], + project_id="" +) +print(response) +# EmbeddingResponse(model='ibm/slate-30m-english-rtrvr', data=[{'object': 'embedding', 'index': 0, 'embedding': [-0.037463713, -0.02141933, -0.02851813, 0.015519324, ..., -0.0021367231, -0.01704561, -0.001425816, 0.0035238306]}], object='list', usage=Usage(prompt_tokens=8, total_tokens=8)) +``` + +## OpenAI Proxy Usage + +Here's how to call IBM watsonx.ai with the LiteLLM Proxy Server + +### 1. Save keys in your environment + +```bash +export WATSONX_URL="" +export WATSONX_APIKEY="" +export WATSONX_PROJECT_ID="" +``` + +### 2. Start the proxy + + + + +```bash +$ litellm --model watsonx/meta-llama/llama-3-8b-instruct + +# Server running on http://0.0.0.0:4000 +``` + + + + +```yaml +model_list: + - model_name: llama-3-8b + litellm_params: + # all params accepted by litellm.completion() + model: watsonx/meta-llama/llama-3-8b-instruct + api_key: "os.environ/WATSONX_API_KEY" # does os.getenv("WATSONX_API_KEY") +``` + + + +### 3. Test it + + + + + +```shell +curl --location 'http://0.0.0.0:4000/chat/completions' \ +--header 'Content-Type: application/json' \ +--data ' { + "model": "llama-3-8b", + "messages": [ + { + "role": "user", + "content": "what is your favorite colour?" + } + ] + } +' +``` + + + +```python +import openai +client = openai.OpenAI( + api_key="anything", + base_url="http://0.0.0.0:4000" +) + +# request sent to model set on litellm proxy, `litellm --model` +response = client.chat.completions.create(model="llama-3-8b", messages=[ + { + "role": "user", + "content": "what is your favorite colour?" + } +]) + +print(response) + +``` + + + +```python +from langchain.chat_models import ChatOpenAI +from langchain.prompts.chat import ( + ChatPromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate, +) +from langchain.schema import HumanMessage, SystemMessage + +chat = ChatOpenAI( + openai_api_base="http://0.0.0.0:4000", # set openai_api_base to the LiteLLM Proxy + model = "llama-3-8b", + temperature=0.1 +) + +messages = [ + SystemMessage( + content="You are a helpful assistant that im using to make a test request to." + ), + HumanMessage( + content="test from litellm. tell me why it's amazing in 1 sentence" + ), +] +response = chat(messages) + +print(response) +``` + + + + +## Authentication + +### Passing credentials as parameters + +You can also pass the credentials as parameters to the completion and embedding functions. + +```python +import os +from litellm import completion + +response = completion( + model="watsonx/ibm/granite-13b-chat-v2", + messages=[{ "content": "What is your favorite color?","role": "user"}], + url="", + api_key="", + project_id="" +) +``` + + +## Supported IBM watsonx.ai Models + +Here are some examples of models available in IBM watsonx.ai that you can use with LiteLLM: + +| Mode Name | Command | +| ---------- | --------- | +| Flan T5 XXL | `completion(model=watsonx/google/flan-t5-xxl, messages=messages)` | +| Flan Ul2 | `completion(model=watsonx/google/flan-ul2, messages=messages)` | +| Mt0 XXL | `completion(model=watsonx/bigscience/mt0-xxl, messages=messages)` | +| Gpt Neox | `completion(model=watsonx/eleutherai/gpt-neox-20b, messages=messages)` | +| Mpt 7B Instruct2 | `completion(model=watsonx/ibm/mpt-7b-instruct2, messages=messages)` | +| Starcoder | `completion(model=watsonx/bigcode/starcoder, messages=messages)` | +| Llama 2 70B Chat | `completion(model=watsonx/meta-llama/llama-2-70b-chat, messages=messages)` | +| Llama 2 13B Chat | `completion(model=watsonx/meta-llama/llama-2-13b-chat, messages=messages)` | +| Granite 13B Instruct | `completion(model=watsonx/ibm/granite-13b-instruct-v1, messages=messages)` | +| Granite 13B Chat | `completion(model=watsonx/ibm/granite-13b-chat-v1, messages=messages)` | +| Flan T5 XL | `completion(model=watsonx/google/flan-t5-xl, messages=messages)` | +| Granite 13B Chat V2 | `completion(model=watsonx/ibm/granite-13b-chat-v2, messages=messages)` | +| Granite 13B Instruct V2 | `completion(model=watsonx/ibm/granite-13b-instruct-v2, messages=messages)` | +| Elyza Japanese Llama 2 7B Instruct | `completion(model=watsonx/elyza/elyza-japanese-llama-2-7b-instruct, messages=messages)` | +| Mixtral 8X7B Instruct V01 Q | `completion(model=watsonx/ibm-mistralai/mixtral-8x7b-instruct-v01-q, messages=messages)` | + + +For a list of all available models in watsonx.ai, see [here](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx&locale=en&audience=wdp). + + +## Supported IBM watsonx.ai Embedding Models + +| Model Name | Function Call | +|----------------------|---------------------------------------------| +| Slate 30m | `embedding(model="watsonx/ibm/slate-30m-english-rtrvr", input=input)` | +| Slate 125m | `embedding(model="watsonx/ibm/slate-125m-english-rtrvr", input=input)` | + + +For a list of all available embedding models in watsonx.ai, see [here](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models-embed.html?context=wx). \ No newline at end of file diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 0fb4ac027f4..bbc0ad26d4c 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -148,6 +148,7 @@ const sidebars = { "providers/openrouter", "providers/custom_openai_proxy", "providers/petals", + "providers/watsonx", ], }, "proxy/custom_pricing", From 160acc085a95be55dd73109fd7593f7438a61259 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 25 Apr 2024 11:57:27 -0700 Subject: [PATCH 13/62] fix(router.py): fix default retry logic --- .gitignore | 1 + litellm/llms/openai.py | 1 + litellm/proxy/_super_secret_config.yaml | 47 ++----------------------- litellm/router.py | 24 +++++++++---- litellm/tests/test_router.py | 41 ++++++++++++++++++++- litellm/types/router.py | 6 ++-- 6 files changed, 63 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index 357f3e1bf8e..abc4ecb0ced 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,4 @@ loadtest_kub.yaml litellm/proxy/_new_secret_config.yaml litellm/proxy/_new_secret_config.yaml litellm/proxy/_super_secret_config.yaml +litellm/proxy/_super_secret_config.yaml diff --git a/litellm/llms/openai.py b/litellm/llms/openai.py index e3c012dabbe..f68ab235e6a 100644 --- a/litellm/llms/openai.py +++ b/litellm/llms/openai.py @@ -447,6 +447,7 @@ class OpenAIChatCompletion(BaseLLM): ) else: openai_aclient = client + ## LOGGING logging_obj.pre_call( input=data["messages"], diff --git a/litellm/proxy/_super_secret_config.yaml b/litellm/proxy/_super_secret_config.yaml index 9372d4ca829..bccc69e19e4 100644 --- a/litellm/proxy/_super_secret_config.yaml +++ b/litellm/proxy/_super_secret_config.yaml @@ -1,51 +1,8 @@ -environment_variables: - SLACK_WEBHOOK_URL: SQD2/FQHvDuj6Q9/Umyqi+EKLNKKLRCXETX2ncO0xCIQp6EHCKiYD7jPW0+1QdrsQ+pnEzhsfVY2r21SiQV901n/9iyJ2tSnEyWViP7FKQVtTvwutsAqSqbiVHxLHbpjPCu03fhS/idjZrtK7dJLbLBB3RgudjNjHg== -general_settings: - alerting: - - slack - alerting_threshold: 300 - database_connection_pool_limit: 100 - database_connection_timeout: 60 - health_check_interval: 300 - proxy_batch_write_at: 10 - ui_access_mode: all -litellm_settings: - allowed_fails: 3 - failure_callback: - - prometheus - fallbacks: - - gpt-3.5-turbo: - - fake-openai-endpoint - - gpt-4 - num_retries: 3 - service_callback: - - prometheus_system - success_callback: - - prometheus model_list: - litellm_params: - api_base: https://openai-function-calling-workers.tasslexyz.workers.dev/ + api_base: http://0.0.0.0:8080 api_key: my-fake-key model: openai/my-fake-model model_name: fake-openai-endpoint -- litellm_params: - model: gpt-3.5-turbo - model_name: gpt-3.5-turbo -- model_name: llama-3 - litellm_params: - model: replicate/meta/meta-llama-3-8b-instruct router_settings: - allowed_fails: 3 - context_window_fallbacks: null - cooldown_time: 1 - fallbacks: - - gpt-3.5-turbo: - - fake-openai-endpoint - - gpt-4 - - gpt-3.5-turbo-3: - - fake-openai-endpoint - num_retries: 3 - retry_after: 0 - routing_strategy: simple-shuffle - routing_strategy_args: {} - timeout: 6000 + num_retries: 0 diff --git a/litellm/router.py b/litellm/router.py index 371d8e8ebdd..1c2bb44642d 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -50,7 +50,7 @@ class Router: model_names: List = [] cache_responses: Optional[bool] = False default_cache_time_seconds: int = 1 * 60 * 60 # 1 hour - num_retries: int = 0 + num_retries: int = openai.DEFAULT_MAX_RETRIES tenacity = None leastbusy_logger: Optional[LeastBusyLoggingHandler] = None lowesttpm_logger: Optional[LowestTPMLoggingHandler] = None @@ -70,7 +70,7 @@ class Router: ] = None, # if you want to cache across model groups client_ttl: int = 3600, # ttl for cached clients - will re-initialize after this time in seconds ## RELIABILITY ## - num_retries: int = 0, + num_retries: Optional[int] = None, timeout: Optional[float] = None, default_litellm_params={}, # default params for Router.chat.completion.create default_max_parallel_requests: Optional[int] = None, @@ -229,7 +229,12 @@ class Router: self.failed_calls = ( InMemoryCache() ) # cache to track failed call per deployment, if num failed calls within 1 minute > allowed fails, then add it to cooldown - self.num_retries = num_retries or litellm.num_retries or 0 + + if num_retries is not None: + self.num_retries = num_retries + elif litellm.num_retries is not None: + self.num_retries = litellm.num_retries + self.timeout = timeout or litellm.request_timeout self.retry_after = retry_after @@ -428,6 +433,7 @@ class Router: kwargs["messages"] = messages kwargs["original_function"] = self._acompletion kwargs["num_retries"] = kwargs.get("num_retries", self.num_retries) + timeout = kwargs.get("request_timeout", self.timeout) kwargs.setdefault("metadata", {}).update({"model_group": model}) @@ -1415,10 +1421,12 @@ class Router: context_window_fallbacks = kwargs.pop( "context_window_fallbacks", self.context_window_fallbacks ) - verbose_router_logger.debug( - f"async function w/ retries: original_function - {original_function}" - ) + num_retries = kwargs.pop("num_retries") + + verbose_router_logger.debug( + f"async function w/ retries: original_function - {original_function}, num_retries - {num_retries}" + ) try: # if the function call is successful, no exception will be raised and we'll break out of the loop response = await original_function(*args, **kwargs) @@ -1986,7 +1994,9 @@ class Router: stream_timeout = litellm.get_secret(stream_timeout_env_name) litellm_params["stream_timeout"] = stream_timeout - max_retries = litellm_params.pop("max_retries", 2) + max_retries = 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 = litellm.get_secret(max_retries_env_name) diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index 7beb1d67c7b..ed486d6f5df 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -1,7 +1,7 @@ #### What this tests #### # This tests litellm router -import sys, os, time +import sys, os, time, openai import traceback, asyncio import pytest @@ -18,6 +18,45 @@ from dotenv import load_dotenv load_dotenv() +@pytest.mark.parametrize("num_retries", [None, 2]) +@pytest.mark.parametrize("max_retries", [None, 4]) +def test_router_num_retries_init(num_retries, max_retries): + """ + - test when num_retries set v/s not + - test client value when max retries set v/s not + """ + router = Router( + model_list=[ + { + "model_name": "gpt-3.5-turbo", # openai model name + "litellm_params": { # params for litellm completion/embedding call + "model": "azure/chatgpt-v-2", + "api_key": "bad-key", + "api_version": os.getenv("AZURE_API_VERSION"), + "api_base": os.getenv("AZURE_API_BASE"), + "max_retries": max_retries, + }, + "model_info": {"id": 12345}, + }, + ], + num_retries=num_retries, + ) + + if num_retries is not None: + assert router.num_retries == num_retries + else: + assert router.num_retries == openai.DEFAULT_MAX_RETRIES + + model_client = router._get_client( + {"model_info": {"id": 12345}}, client_type="async", kwargs={} + ) + + if max_retries is not None: + assert getattr(model_client, "max_retries") == max_retries + else: + assert getattr(model_client, "max_retries") == 0 + + def test_exception_raising(): # this tests if the router raises an exception when invalid params are set # in this test both deployments have bad keys - Keep this test. It validates if the router raises the most recent exception diff --git a/litellm/types/router.py b/litellm/types/router.py index c5ec47091a1..1bd8bda97a2 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -108,7 +108,7 @@ class LiteLLM_Params(BaseModel): stream_timeout: Optional[Union[float, str]] = ( None # timeout when making stream=True calls, if str, pass in as os.environ/ ) - max_retries: int = 2 # follows openai default of 2 + max_retries: Optional[int] = None organization: Optional[str] = None # for openai orgs ## VERTEX AI ## vertex_project: Optional[str] = None @@ -146,9 +146,7 @@ class LiteLLM_Params(BaseModel): args.pop("self", None) args.pop("params", None) args.pop("__class__", None) - if max_retries is None: - max_retries = 2 - elif isinstance(max_retries, str): + if max_retries is not None and isinstance(max_retries, str): max_retries = int(max_retries) # cast to int super().__init__(max_retries=max_retries, **args, **params) From a81945464702e708432b04716040b9bea0f636d8 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 25 Apr 2024 13:31:19 -0700 Subject: [PATCH 14/62] test(test_completion.py): fix test to not raise exception if it works --- litellm/tests/test_completion.py | 1 - 1 file changed, 1 deletion(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 1f12f75eec3..1d30f8829bc 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -1781,7 +1781,6 @@ def test_completion_replicate_llama3(): print("RESPONSE STRING\n", response_str) if type(response_str) != str: pytest.fail(f"Error occurred: {e}") - raise Exception("it worked!") except Exception as e: pytest.fail(f"Error occurred: {e}") From 54241f25516013f06d016aa21ac4703f78275d42 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 25 Apr 2024 17:43:40 -0700 Subject: [PATCH 15/62] test(test_router_fallbacks.py): fix testing --- litellm/llms/prompt_templates/factory.py | 5 +---- litellm/tests/test_router_fallbacks.py | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index a6d1d64386f..1a576f43a33 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -1359,11 +1359,8 @@ def prompt_factory( "meta-llama/llama-3" in model or "meta-llama-3" in model ) and "instruct" in model: return hf_chat_template( - model=model, + model="meta-llama/Meta-Llama-3-8B-Instruct", messages=messages, - chat_template=known_tokenizer_config[ # type: ignore - "meta-llama/Meta-Llama-3-8B-Instruct" - ]["tokenizer"]["chat_template"], ) elif ( "tiiuae/falcon" in model diff --git a/litellm/tests/test_router_fallbacks.py b/litellm/tests/test_router_fallbacks.py index 98a2449f06b..51d9451a87e 100644 --- a/litellm/tests/test_router_fallbacks.py +++ b/litellm/tests/test_router_fallbacks.py @@ -258,6 +258,7 @@ def test_sync_fallbacks_embeddings(): model_list=model_list, fallbacks=[{"bad-azure-embedding-model": ["good-azure-embedding-model"]}], set_verbose=False, + num_retries=0, ) customHandler = MyCustomHandler() litellm.callbacks = [customHandler] @@ -393,7 +394,7 @@ def test_dynamic_fallbacks_sync(): }, ] - router = Router(model_list=model_list, set_verbose=True) + router = Router(model_list=model_list, set_verbose=True, num_retries=0) kwargs = {} kwargs["model"] = "azure/gpt-3.5-turbo" kwargs["messages"] = [{"role": "user", "content": "Hey, how's it going?"}] From 19852310220fe8327f60f81753972de30d6e4885 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 25 Apr 2024 18:06:25 -0700 Subject: [PATCH 16/62] test(test_timeout.py): explicitly set num retries = 0 --- litellm/tests/test_timeout.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_timeout.py b/litellm/tests/test_timeout.py index 8c92607c02c..25968916778 100644 --- a/litellm/tests/test_timeout.py +++ b/litellm/tests/test_timeout.py @@ -78,7 +78,8 @@ def test_hanging_request_azure(): "model_name": "openai-gpt", "litellm_params": {"model": "gpt-3.5-turbo"}, }, - ] + ], + num_retries=0, ) encoded = litellm.utils.encode(model="gpt-3.5-turbo", text="blue")[0] From 4c5398b556fbedfdf4389ec23e6af53ac389ff97 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Thu, 25 Apr 2024 19:35:30 -0700 Subject: [PATCH 17/62] test(test_timeout.py): fix test --- litellm/tests/test_timeout.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/tests/test_timeout.py b/litellm/tests/test_timeout.py index 25968916778..d38da52e51f 100644 --- a/litellm/tests/test_timeout.py +++ b/litellm/tests/test_timeout.py @@ -132,7 +132,8 @@ def test_hanging_request_openai(): "model_name": "openai-gpt", "litellm_params": {"model": "gpt-3.5-turbo"}, }, - ] + ], + num_retries=0, ) encoded = litellm.utils.encode(model="gpt-3.5-turbo", text="blue")[0] @@ -190,6 +191,7 @@ def test_timeout_streaming(): # test_timeout_streaming() +@pytest.mark.skip(reason="local test") def test_timeout_ollama(): # this Will Raise a timeout import litellm From 6fedcb873b7a6fe6837004bfaf0e888532f7ebf2 Mon Sep 17 00:00:00 2001 From: pwm Date: Fri, 26 Apr 2024 14:35:01 +0800 Subject: [PATCH 18/62] add safety_settings parameters to Vertex vision async_completion function --- litellm/llms/vertex_ai.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/litellm/llms/vertex_ai.py b/litellm/llms/vertex_ai.py index 24108e46635..e8e9ca5827c 100644 --- a/litellm/llms/vertex_ai.py +++ b/litellm/llms/vertex_ai.py @@ -529,6 +529,7 @@ def completion( "instances": instances, "vertex_location": vertex_location, "vertex_project": vertex_project, + "safety_settings":safety_settings, **optional_params, } if optional_params.get("stream", False) is True: @@ -813,6 +814,7 @@ async def async_completion( instances=None, vertex_project=None, vertex_location=None, + safety_settings=None, **optional_params, ): """ @@ -844,6 +846,7 @@ async def async_completion( response = await llm_model._generate_content_async( contents=content, generation_config=optional_params, + safety_settings=safety_settings, tools=tools, ) From 644d8c71b84fc749bbfeddab048e0ad9f0516371 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Fri, 26 Apr 2024 10:23:15 -0700 Subject: [PATCH 19/62] docs - setting up litellm-database --- docs/my-website/docs/proxy/deploy.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md index 7e350bfa9ce..992350211e8 100644 --- a/docs/my-website/docs/proxy/deploy.md +++ b/docs/my-website/docs/proxy/deploy.md @@ -231,13 +231,16 @@ Your OpenAI proxy server is now running on `http://127.0.0.1:4000`. | Docs | When to Use | | --- | --- | | [Quick Start](#quick-start) | call 100+ LLMs + Load Balancing | -| [Deploy with Database](#deploy-with-database) | + use Virtual Keys + Track Spend | +| [Deploy with Database](#deploy-with-database) | + use Virtual Keys + Track Spend (Note: When deploying with a database providing a `DATABASE_URL` and `LITELLM_MASTER_KEY` are required in your env ) | | [LiteLLM container + Redis](#litellm-container--redis) | + load balance across multiple litellm containers | | [LiteLLM Database container + PostgresDB + Redis](#litellm-database-container--postgresdb--redis) | + use Virtual Keys + Track Spend + load balance across multiple litellm containers | ## Deploy with Database ### Docker, Kubernetes, Helm Chart +Requirements: +- Need a postgres database (e.g. [Supabase](https://supabase.com/), [Neon](https://neon.tech/), etc) Set `DATABASE_URL=postgresql://:@:/` in your env +- Set a `LITELLM_MASTER_KEY`, this is your Proxy Admin key - you can use this to create other keys (🚨 must start with `sk-`) @@ -252,6 +255,8 @@ docker pull ghcr.io/berriai/litellm-database:main-latest ```shell docker run \ -v $(pwd)/litellm_config.yaml:/app/config.yaml \ + -e LITELLM_MASTER_KEY=sk-1234 \ + -e DATABASE_URL=postgresql://:@:/ \ -e AZURE_API_KEY=d6*********** \ -e AZURE_API_BASE=https://openai-***********/ \ -p 4000:4000 \ From 7730520fb03f97f06203ff6b12e8284a93e065ac Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 14:56:58 -0700 Subject: [PATCH 20/62] fix(router.py): allow passing httpx.timeout to timeout param in router Closes https://github.com/BerriAI/litellm/issues/3162 --- litellm/tests/test_router.py | 31 +++++++++++++++++++++++++++++++ litellm/types/router.py | 7 +++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index 7beb1d67c7b..26843b50b94 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -14,10 +14,41 @@ from litellm.router import Deployment, LiteLLM_Params, ModelInfo from concurrent.futures import ThreadPoolExecutor from collections import defaultdict from dotenv import load_dotenv +import os, httpx load_dotenv() +@pytest.mark.parametrize( + "timeout", [10, 1.0, httpx.Timeout(timeout=300.0, connect=20.0)] +) +def test_router_timeout_init(timeout): + """ + Allow user to pass httpx.Timeout + + related issue - https://github.com/BerriAI/litellm/issues/3162 + """ + + router = Router( + model_list=[ + { + "model_name": "test-model", + "litellm_params": { + "model": "azure/chatgpt-v-2", + "api_key": os.getenv("AZURE_API_KEY"), + "api_base": os.getenv("AZURE_API_BASE"), + "api_version": os.getenv("AZURE_API_VERSION"), + "timeout": timeout, + }, + } + ] + ) + + router.completion( + model="test-model", messages=[{"role": "user", "content": "Hey!"}] + ) + + def test_exception_raising(): # this tests if the router raises an exception when invalid params are set # in this test both deployments have bad keys - Keep this test. It validates if the router raises the most recent exception diff --git a/litellm/types/router.py b/litellm/types/router.py index c5ec47091a1..7fa15ac365e 100644 --- a/litellm/types/router.py +++ b/litellm/types/router.py @@ -1,5 +1,5 @@ from typing import List, Optional, Union, Dict, Tuple, Literal - +import httpx from pydantic import BaseModel, validator from .completion import CompletionRequest from .embedding import EmbeddingRequest @@ -104,7 +104,9 @@ class LiteLLM_Params(BaseModel): api_key: Optional[str] = None api_base: Optional[str] = None api_version: Optional[str] = None - timeout: Optional[Union[float, str]] = None # if str, pass in as os.environ/ + timeout: Optional[Union[float, str, httpx.Timeout]] = ( + None # if str, pass in as os.environ/ + ) stream_timeout: Optional[Union[float, str]] = ( None # timeout when making stream=True calls, if str, pass in as os.environ/ ) @@ -154,6 +156,7 @@ class LiteLLM_Params(BaseModel): class Config: extra = "allow" + arbitrary_types_allowed = True def __contains__(self, key): # Define custom behavior for the 'in' operator From 180718c33f5e688b24098155b92149e862e9935a Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 15:38:01 -0700 Subject: [PATCH 21/62] fix(router.py): support verify_ssl flag Fixes https://github.com/BerriAI/litellm/issues/3162#issuecomment-2075273807 --- litellm/__init__.py | 1 + litellm/router.py | 96 ++++++++++++++++++++++-------------- litellm/tests/test_router.py | 22 +++++++-- 3 files changed, 80 insertions(+), 39 deletions(-) diff --git a/litellm/__init__.py b/litellm/__init__.py index b9d9891ca25..75a6751b08a 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -77,6 +77,7 @@ baseten_key: Optional[str] = None aleph_alpha_key: Optional[str] = None nlp_cloud_key: Optional[str] = None use_client: bool = False +ssl_verify: bool = True disable_streaming_logging: bool = False ### GUARDRAILS ### llamaguard_model_name: Optional[str] = None diff --git a/litellm/router.py b/litellm/router.py index 371d8e8ebdd..8cb0f3ed2a1 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -2052,9 +2052,11 @@ class Router: timeout=timeout, max_retries=max_retries, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), # type: ignore @@ -2074,9 +2076,11 @@ class Router: timeout=timeout, max_retries=max_retries, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=sync_proxy_mounts, ), # type: ignore @@ -2096,9 +2100,11 @@ class Router: timeout=stream_timeout, max_retries=max_retries, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), # type: ignore @@ -2118,9 +2124,11 @@ class Router: timeout=stream_timeout, max_retries=max_retries, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=sync_proxy_mounts, ), # type: ignore @@ -2158,9 +2166,11 @@ class Router: timeout=timeout, max_retries=max_retries, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), # type: ignore @@ -2178,9 +2188,11 @@ class Router: timeout=timeout, max_retries=max_retries, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + verify=litellm.ssl_verify, + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), ), mounts=sync_proxy_mounts, ), # type: ignore @@ -2199,9 +2211,11 @@ class Router: timeout=stream_timeout, max_retries=max_retries, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), @@ -2219,9 +2233,11 @@ class Router: timeout=stream_timeout, max_retries=max_retries, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=sync_proxy_mounts, ), @@ -2249,9 +2265,11 @@ class Router: max_retries=max_retries, organization=organization, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), # type: ignore @@ -2271,9 +2289,11 @@ class Router: max_retries=max_retries, organization=organization, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=sync_proxy_mounts, ), # type: ignore @@ -2294,9 +2314,11 @@ class Router: max_retries=max_retries, organization=organization, http_client=httpx.AsyncClient( - transport=AsyncCustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=AsyncCustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=async_proxy_mounts, ), # type: ignore @@ -2317,9 +2339,11 @@ class Router: max_retries=max_retries, organization=organization, http_client=httpx.Client( - transport=CustomHTTPTransport(), - limits=httpx.Limits( - max_connections=1000, max_keepalive_connections=100 + transport=CustomHTTPTransport( + limits=httpx.Limits( + max_connections=1000, max_keepalive_connections=100 + ), + verify=litellm.ssl_verify, ), mounts=sync_proxy_mounts, ), # type: ignore diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index 26843b50b94..fd95083b7bf 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -22,12 +22,14 @@ load_dotenv() @pytest.mark.parametrize( "timeout", [10, 1.0, httpx.Timeout(timeout=300.0, connect=20.0)] ) -def test_router_timeout_init(timeout): +@pytest.mark.parametrize("ssl_verify", [True, False]) +def test_router_timeout_init(timeout, ssl_verify): """ Allow user to pass httpx.Timeout related issue - https://github.com/BerriAI/litellm/issues/3162 """ + litellm.ssl_verify = ssl_verify router = Router( model_list=[ @@ -40,14 +42,28 @@ def test_router_timeout_init(timeout): "api_version": os.getenv("AZURE_API_VERSION"), "timeout": timeout, }, + "model_info": {"id": 1234}, } ] ) - router.completion( - model="test-model", messages=[{"role": "user", "content": "Hey!"}] + model_client = router._get_client( + deployment={"model_info": {"id": 1234}}, client_type="sync_client", kwargs={} ) + assert getattr(model_client, "timeout") == timeout + + print(f"vars model_client: {vars(model_client)}") + http_client = getattr(model_client, "_client") + print(f"http client: {vars(http_client)}, ssl_Verify={ssl_verify}") + if ssl_verify == False: + assert http_client._transport._pool._ssl_context.verify_mode.name == "CERT_NONE" + else: + assert ( + http_client._transport._pool._ssl_context.verify_mode.name + == "CERT_REQUIRED" + ) + def test_exception_raising(): # this tests if the router raises an exception when invalid params are set From e05764bdb7dda49127dd4b1c2c4d02fa90463e71 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 17:05:07 -0700 Subject: [PATCH 22/62] fix(router.py): add `/v1/` if missing to base url, for openai-compatible api's Fixes https://github.com/BerriAI/litellm/issues/2279 --- litellm/proxy/_super_secret_config.yaml | 5 ++++ litellm/router.py | 18 +++++++++++++ litellm/tests/test_router.py | 36 +++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/litellm/proxy/_super_secret_config.yaml b/litellm/proxy/_super_secret_config.yaml index 0ea72c85b1a..89827df7c1c 100644 --- a/litellm/proxy/_super_secret_config.yaml +++ b/litellm/proxy/_super_secret_config.yaml @@ -13,6 +13,11 @@ model_list: - litellm_params: model: gpt-4 model_name: gpt-4 +- model_name: azure-mistral + litellm_params: + model: azure/mistral-large-latest + api_base: https://Mistral-large-nmefg-serverless.eastus2.inference.ai.azure.com + api_key: os.environ/AZURE_MISTRAL_API_KEY # litellm_settings: # cache: True \ No newline at end of file diff --git a/litellm/router.py b/litellm/router.py index 8cb0f3ed2a1..f84b2eab026 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1929,6 +1929,7 @@ class Router: ) 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 @@ -1964,6 +1965,23 @@ class Router: api_base = litellm.get_secret(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 ( + custom_llm_provider == "openai" + and api_base is not None + and not api_base.endswith("/v1/") + ): + # check if it ends with a trailing slash + if api_base.endswith("/"): + api_base += "v1/" + 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/", "") diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index fd95083b7bf..914e36da1a0 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -65,6 +65,42 @@ def test_router_timeout_init(timeout, ssl_verify): ) +@pytest.mark.parametrize( + "mistral_api_base", + [ + "os.environ/AZURE_MISTRAL_API_BASE", + "https://Mistral-large-nmefg-serverless.eastus2.inference.ai.azure.com/v1/", + "https://Mistral-large-nmefg-serverless.eastus2.inference.ai.azure.com/v1", + "https://Mistral-large-nmefg-serverless.eastus2.inference.ai.azure.com/", + "https://Mistral-large-nmefg-serverless.eastus2.inference.ai.azure.com", + ], +) +def test_router_azure_ai_studio_init(mistral_api_base): + router = Router( + model_list=[ + { + "model_name": "test-model", + "litellm_params": { + "model": "azure/mistral-large-latest", + "api_key": "os.environ/AZURE_MISTRAL_API_KEY", + "api_base": mistral_api_base, + }, + "model_info": {"id": 1234}, + } + ] + ) + + model_client = router._get_client( + deployment={"model_info": {"id": 1234}}, client_type="sync_client", kwargs={} + ) + url = getattr(model_client, "_base_url") + uri_reference = str(getattr(url, "_uri_reference")) + + print(f"uri_reference: {uri_reference}") + + assert "/v1/" in uri_reference + + def test_exception_raising(): # this tests if the router raises an exception when invalid params are set # in this test both deployments have bad keys - Keep this test. It validates if the router raises the most recent exception From 93463565fb15a15e6b97a97c28f317996bba97ef Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 17:11:21 -0700 Subject: [PATCH 23/62] fix(replicate.py): pass version if passed in --- litellm/llms/replicate.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/litellm/llms/replicate.py b/litellm/llms/replicate.py index c1456bd3f22..65052e3179a 100644 --- a/litellm/llms/replicate.py +++ b/litellm/llms/replicate.py @@ -112,10 +112,16 @@ def start_prediction( } initial_prediction_data = { - "version": version_id, "input": input_data, } + if ":" in version_id and len(version_id) > 64: + model_parts = version_id.split(":") + if ( + len(model_parts) > 1 and len(model_parts[1]) == 64 + ): ## checks if model name has a 64 digit code - e.g. "meta/llama-2-70b-chat:02e509c789964a7ea8736978a43525956ef40397be9033abf9fd2badfe68c9e3" + initial_prediction_data["version"] = model_parts[1] + ## LOGGING logging_obj.pre_call( input=input_data["prompt"], From 2aa4976b82e3840c5891e37247e2c2d2835fb31c Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 17:16:25 -0700 Subject: [PATCH 24/62] docs(mistral.md): update to show tool calling example --- docs/my-website/docs/providers/mistral.md | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/my-website/docs/providers/mistral.md b/docs/my-website/docs/providers/mistral.md index b6558435b8b..8e5e2bf66b7 100644 --- a/docs/my-website/docs/providers/mistral.md +++ b/docs/my-website/docs/providers/mistral.md @@ -53,6 +53,50 @@ All models listed here https://docs.mistral.ai/platform/endpoints are supported. | open-mixtral-8x22b | `completion(model="mistral/open-mixtral-8x22b", messages)` | +## Function Calling + +```python +from litellm import completion + +# set env +os.environ["MISTRAL_API_KEY"] = "your-api-key" + +tools = [ + { + "type": "function", + "function": { + "name": "get_current_weather", + "description": "Get the current weather in a given location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The city and state, e.g. San Francisco, CA", + }, + "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, + }, + "required": ["location"], + }, + }, + } +] +messages = [{"role": "user", "content": "What's the weather like in Boston today?"}] + +response = completion( + model="mistral/mistral-large-latest", + messages=messages, + tools=tools, + tool_choice="auto", +) +# Add any assertions, here to check response args +print(response) +assert isinstance(response.choices[0].message.tool_calls[0].function.name, str) +assert isinstance( + response.choices[0].message.tool_calls[0].function.arguments, str +) +``` + ## Sample Usage - Embedding ```python from litellm import embedding From 487652fd0a675ae497a4d06a9954143ba417ae37 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Fri, 26 Apr 2024 22:42:16 -0700 Subject: [PATCH 25/62] =?UTF-8?q?bump:=20version=201.35.29=20=E2=86=92=201?= =?UTF-8?q?.35.30?= 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 0b8b7359cd3..2bd77b6c97d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.35.29" +version = "1.35.30" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -80,7 +80,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.35.29" +version = "1.35.30" version_files = [ "pyproject.toml:^version" ] From 2ecbf6663a70dbee13520fda133f74142a9df017 Mon Sep 17 00:00:00 2001 From: Emir Ayar Date: Sat, 27 Apr 2024 12:27:12 +0200 Subject: [PATCH 26/62] Add test for completion with text content dictionaries --- litellm/tests/test_completion.py | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index d2c004a0ab1..e2c635a5cb7 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -230,6 +230,48 @@ def test_completion_claude_3_function_call(): except Exception as e: pytest.fail(f"Error occurred: {e}") +def test_completion_claude_3_with_text_content_dictionaries(): + litellm.set_verbose = True + messages = [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello" + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Hello! How can I assist you today?" + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "Hello again!" + } + ] + } + ] + + try: + # test without max tokens + response = completion( + model="anthropic/claude-3-opus-20240229", + messages=messages, + ) + # Add any assertions, here to check response args + print(response) + except Exception as e: + pytest.fail(f"Error occurred: {e}") def test_parse_xml_params(): from litellm.llms.prompt_templates.factory import parse_xml_params From 8ff9555bcf78c30371e08a2547a600f0ed9ed9ca Mon Sep 17 00:00:00 2001 From: Tejas Ravishankar Date: Sat, 27 Apr 2024 17:47:28 +0400 Subject: [PATCH 27/62] fix: duplicate mention of `VERTEXAI_PROJECT` environment variable causing confusion --- litellm/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/utils.py b/litellm/utils.py index 9b91ab36dde..9f176c19490 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -6563,7 +6563,7 @@ def validate_environment(model: Optional[str] = None) -> dict: if "VERTEXAI_PROJECT" in os.environ and "VERTEXAI_LOCATION" in os.environ: keys_in_environment = True else: - missing_keys.extend(["VERTEXAI_PROJECT", "VERTEXAI_PROJECT"]) + missing_keys.extend(["VERTEXAI_PROJECT", "VERTEXAI_LOCATION"]) elif custom_llm_provider == "huggingface": if "HUGGINGFACE_API_KEY" in os.environ: keys_in_environment = True From 63e8209fe5b42e7bc65555c3acf2eb1e98538f2e Mon Sep 17 00:00:00 2001 From: "Simon S. Viloria" Date: Sat, 27 Apr 2024 16:43:17 +0200 Subject: [PATCH 28/62] (docs) added watsonx to the list of supported providers --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3caeb830bb6..38a16693578 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,7 @@ curl 'http://0.0.0.0:4000/key/generate' \ | [perplexity-ai](https://docs.litellm.ai/docs/providers/perplexity) | ✅ | ✅ | ✅ | ✅ | | [Groq AI](https://docs.litellm.ai/docs/providers/groq) | ✅ | ✅ | ✅ | ✅ | | [anyscale](https://docs.litellm.ai/docs/providers/anyscale) | ✅ | ✅ | ✅ | ✅ | +| [IBM - watsonx.ai](https://docs.litellm.ai/docs/providers/watsonx) | ✅ | ✅ | ✅ | ✅ | ✅ | [voyage ai](https://docs.litellm.ai/docs/providers/voyage) | | | | | ✅ | | [xinference [Xorbits Inference]](https://docs.litellm.ai/docs/providers/xinference) | | | | | ✅ | From 3c248e308ec1bbbd7cb24c48e8200db27b671fca Mon Sep 17 00:00:00 2001 From: "Simon S. Viloria" Date: Sat, 27 Apr 2024 16:43:49 +0200 Subject: [PATCH 29/62] (docs) fixed typo in url for watsonx colab notebook --- docs/my-website/docs/providers/watsonx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/my-website/docs/providers/watsonx.md b/docs/my-website/docs/providers/watsonx.md index 9154816a063..d8c5740a861 100644 --- a/docs/my-website/docs/providers/watsonx.md +++ b/docs/my-website/docs/providers/watsonx.md @@ -20,7 +20,7 @@ See [here](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more ## Usage - + Open In Colab From 2a006c3d392c182c792dbe43a37b6ad857dc8746 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Sat, 27 Apr 2024 08:57:18 -0700 Subject: [PATCH 30/62] Revert "Fix Anthropic Messages Prompt Template function to add a third condition: list of text-content dictionaries" --- litellm/llms/prompt_templates/factory.py | 12 ++----- litellm/tests/test_completion.py | 42 ------------------------ 2 files changed, 3 insertions(+), 51 deletions(-) diff --git a/litellm/llms/prompt_templates/factory.py b/litellm/llms/prompt_templates/factory.py index cb1b2eb73d9..c51dc89be5b 100644 --- a/litellm/llms/prompt_templates/factory.py +++ b/litellm/llms/prompt_templates/factory.py @@ -748,15 +748,9 @@ def anthropic_messages_pt_xml(messages: list): assistant_content = [] ## MERGE CONSECUTIVE ASSISTANT CONTENT ## while msg_i < len(messages) and messages[msg_i]["role"] == "assistant": - # Handle assistant messages as string, none, or list of text-content dictionaries. - if isinstance(messages[msg_i].get("content"), list): - assistant_text = '' - for content in messages[msg_i]["content"]: - if content.get("type") == "text": - assistant_text += content["text"] - else: - assistant_text = messages[msg_i].get("content") or "" - + assistant_text = ( + messages[msg_i].get("content") or "" + ) # either string or none if messages[msg_i].get( "tool_calls", [] ): # support assistant tool invoke convertion diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index b4e06f596d6..5317108412d 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -230,48 +230,6 @@ def test_completion_claude_3_function_call(): except Exception as e: pytest.fail(f"Error occurred: {e}") -def test_completion_claude_3_with_text_content_dictionaries(): - litellm.set_verbose = True - messages = [ - { - "role": "user", - "content": [ - { - "type": "text", - "text": "Hello" - } - ] - }, - { - "role": "assistant", - "content": [ - { - "type": "text", - "text": "Hello! How can I assist you today?" - } - ] - }, - { - "role": "user", - "content": [ - { - "type": "text", - "text": "Hello again!" - } - ] - } - ] - - try: - # test without max tokens - response = completion( - model="anthropic/claude-3-opus-20240229", - messages=messages, - ) - # Add any assertions, here to check response args - print(response) - except Exception as e: - pytest.fail(f"Error occurred: {e}") def test_parse_xml_params(): from litellm.llms.prompt_templates.factory import parse_xml_params From 8b6d686e5247fb20d9bd8295c494260bb2ce766e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 10:01:09 -0700 Subject: [PATCH 31/62] feat - turn_off_message_logging --- litellm/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/__init__.py b/litellm/__init__.py index b9d9891ca25..9a8b7344772 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -45,6 +45,7 @@ _async_failure_callback: List[Callable] = ( ) # internal variable - async custom callbacks are routed here. pre_call_rules: List[Callable] = [] post_call_rules: List[Callable] = [] +turn_off_message_logging: Optional[bool] = False ## end of callbacks ############# email: Optional[str] = ( From 10da35675f14aecdd1dee8d0d2ad772ba90322db Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 10:03:07 -0700 Subject: [PATCH 32/62] feat- turn off message logging --- litellm/utils.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/litellm/utils.py b/litellm/utils.py index 8c38633449e..4fe23266656 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -1527,6 +1527,32 @@ class Logging: else: callbacks = litellm.success_callback + # check if user opted out of logging message/response to callbacks + if litellm.turn_off_message_logging == True: + # remove messages, prompts, input, response from logging + self.model_call_details["messages"] = "redacted-by-litellm" + self.model_call_details["prompt"] = "" + self.model_call_details["input"] = "" + + # response cleaning + # ChatCompletion Responses + if ( + self.stream + and "complete_streaming_response" in self.model_call_details + ): + _streaming_response = self.model_call_details[ + "complete_streaming_response" + ] + for choice in _streaming_response.choices: + choice.message.content = "redacted-by-litellm" + else: + for choice in result.choices: + choice.message.content = "redacted-by-litellm" + + # Embedding Responses + + # Text Completion Responses + for callback in callbacks: try: litellm_params = self.model_call_details.get("litellm_params", {}) From 743dfdb950ea9e5b6842419ca4d9ef814d9ec048 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 10:03:34 -0700 Subject: [PATCH 33/62] test - redacting messages from langfuse --- litellm/tests/test_alangfuse.py | 72 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/litellm/tests/test_alangfuse.py b/litellm/tests/test_alangfuse.py index c1d8123c7a0..12e5f9587fb 100644 --- a/litellm/tests/test_alangfuse.py +++ b/litellm/tests/test_alangfuse.py @@ -161,40 +161,54 @@ async def make_async_calls(): return total_time -# def test_langfuse_logging_async_text_completion(): -# try: -# pre_langfuse_setup() -# litellm.set_verbose = False -# litellm.success_callback = ["langfuse"] +@pytest.mark.asyncio +@pytest.mark.parametrize("stream", [False, True]) +async def test_langfuse_logging_without_request_response(stream): + try: + import uuid -# async def _test_langfuse(): -# response = await litellm.atext_completion( -# model="gpt-3.5-turbo-instruct", -# prompt="this is a test", -# max_tokens=5, -# temperature=0.7, -# timeout=5, -# user="test_user", -# stream=True -# ) -# async for chunk in response: -# print() -# print(chunk) -# await asyncio.sleep(1) -# return response + _unique_trace_name = f"litellm-test-{str(uuid.uuid4())}" + litellm.set_verbose = True + litellm.turn_off_message_logging = True + litellm.success_callback = ["langfuse"] + response = await litellm.acompletion( + model="gpt-3.5-turbo", + mock_response="It's simple to use and easy to get started", + messages=[{"role": "user", "content": "Hi 👋 - i'm claude"}], + max_tokens=10, + temperature=0.2, + stream=stream, + metadata={"trace_id": _unique_trace_name}, + ) + print(response) + if stream: + async for chunk in response: + print(chunk) -# response = asyncio.run(_test_langfuse()) -# print(f"response: {response}") + await asyncio.sleep(3) -# # # check langfuse.log to see if there was a failed response -# search_logs("langfuse.log") -# except litellm.Timeout as e: -# pass -# except Exception as e: -# pytest.fail(f"An exception occurred - {e}") + import langfuse + langfuse_client = langfuse.Langfuse( + public_key=os.environ["LANGFUSE_PUBLIC_KEY"], + secret_key=os.environ["LANGFUSE_SECRET_KEY"], + ) -# test_langfuse_logging_async_text_completion() + # get trace with _unique_trace_name + trace = langfuse_client.get_generations(trace_id=_unique_trace_name) + + print("trace_from_langfuse", trace) + + _trace_data = trace.data + + assert _trace_data[0].input == {"messages": "redacted-by-litellm"} + assert _trace_data[0].output == { + "role": "assistant", + "content": "redacted-by-litellm", + } + + except Exception as e: + pytest.fail(f"An exception occurred - {e}") @pytest.mark.skip(reason="beta test - checking langfuse output") From c9d7437d162c2304d5ea660360aac10f5d67e81f Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 10:15:27 -0700 Subject: [PATCH 34/62] fix(watsonx.py): use common litellm params for api key, api base, etc. --- litellm/llms/watsonx.py | 74 ++++++++++++++++++++++++++--------------- litellm/main.py | 2 +- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index aa0cb32df12..28061919e6a 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -13,7 +13,7 @@ from .prompt_templates import factory as ptf class WatsonXAIError(Exception): - def __init__(self, status_code, message, url: str = None): + def __init__(self, status_code, message, url: Optional[str] = None): self.status_code = status_code self.message = message url = url or "https://https://us-south.ml.cloud.ibm.com" @@ -73,7 +73,6 @@ class IBMWatsonXAIConfig: repetition_penalty: Optional[float] = None truncate_input_tokens: Optional[int] = None include_stop_sequences: Optional[bool] = False - return_options: Optional[dict] = None return_options: Optional[Dict[str, bool]] = None random_seed: Optional[int] = None # e.g 42 moderations: Optional[dict] = None @@ -161,6 +160,7 @@ def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): ) return prompt + class WatsonXAIEndpoint(str, Enum): TEXT_GENERATION = "/ml/v1/text/generation" TEXT_GENERATION_STREAM = "/ml/v1/text/generation_stream" @@ -171,6 +171,7 @@ class WatsonXAIEndpoint(str, Enum): EMBEDDINGS = "/ml/v1/text/embeddings" PROMPTS = "/ml/v1/prompts" + class IBMWatsonXAI(BaseLLM): """ Class to interface with IBM Watsonx.ai API for text generation and embeddings. @@ -180,7 +181,6 @@ class IBMWatsonXAI(BaseLLM): api_version = "2024-03-13" - def __init__(self) -> None: super().__init__() @@ -190,7 +190,7 @@ class IBMWatsonXAI(BaseLLM): prompt: str, stream: bool, optional_params: dict, - print_verbose: Callable = None, + print_verbose: Optional[Callable] = None, ) -> dict: """ Get the request parameters for text generation. @@ -224,9 +224,9 @@ class IBMWatsonXAI(BaseLLM): ) deployment_id = "/".join(model_id.split("/")[1:]) endpoint = ( - WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION_STREAM + WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION_STREAM.value if stream - else WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION + else WatsonXAIEndpoint.DEPLOYMENT_TEXT_GENERATION.value ) endpoint = endpoint.format(deployment_id=deployment_id) else: @@ -239,27 +239,40 @@ class IBMWatsonXAI(BaseLLM): ) url = api_params["url"].rstrip("/") + endpoint return dict( - method="POST", url=url, headers=headers, - json=payload, params=request_params + method="POST", url=url, headers=headers, json=payload, params=request_params ) - def _get_api_params(self, params: dict, print_verbose: Callable = None) -> dict: + def _get_api_params( + self, params: dict, print_verbose: Optional[Callable] = None + ) -> dict: """ Find watsonx.ai credentials in the params or environment variables and return the headers for authentication. """ # Load auth variables from params - url = params.pop("url", None) + url = params.pop("url", params.pop("api_base", params.pop("base_url", None))) api_key = params.pop("apikey", None) token = params.pop("token", None) - project_id = params.pop("project_id", None) # watsonx.ai project_id + project_id = params.pop( + "project_id", params.pop("watsonx_project", None) + ) # watsonx.ai project_id - allow 'watsonx_project' to be consistent with how vertex project implementation works -> reduce provider-specific params space_id = params.pop("space_id", None) # watsonx.ai deployment space_id region_name = params.pop("region_name", params.pop("region", None)) - wx_credentials = params.pop("wx_credentials", None) + if region_name is None: + region_name = params.pop( + "watsonx_region_name", params.pop("watsonx_region", None) + ) # consistent with how vertex ai + aws regions are accepted + wx_credentials = params.pop( + "wx_credentials", + params.pop( + "watsonx_credentials", None + ), # follow {provider}_credentials, same as vertex ai + ) api_version = params.pop("api_version", IBMWatsonXAI.api_version) # Load auth variables from environment variables if url is None: url = ( - get_secret("WATSONX_URL") + get_secret("WATSONX_API_BASE") # consistent with 'AZURE_API_BASE' + or get_secret("WATSONX_URL") or get_secret("WX_URL") or get_secret("WML_URL") ) @@ -297,7 +310,12 @@ class IBMWatsonXAI(BaseLLM): api_key = wx_credentials.get( "apikey", wx_credentials.get("api_key", api_key) ) - token = wx_credentials.get("token", token) + token = wx_credentials.get( + "token", + wx_credentials.get( + "watsonx_token", token + ), # follow format of {provider}_token, same as azure - e.g. 'azure_ad_token=..' + ) # verify that all required credentials are present if url is None: @@ -342,10 +360,10 @@ class IBMWatsonXAI(BaseLLM): print_verbose: Callable, encoding, logging_obj, - optional_params: Optional[dict] = None, + optional_params: dict, litellm_params: Optional[dict] = None, logger_fn=None, - timeout: float = None, + timeout: Optional[float] = None, ): """ Send a text generation request to the IBM Watsonx.ai API. @@ -379,10 +397,14 @@ class IBMWatsonXAI(BaseLLM): model_response["finish_reason"] = json_resp["results"][0]["stop_reason"] model_response["created"] = int(time.time()) model_response["model"] = model - model_response.usage = Usage( - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, - total_tokens=prompt_tokens + completion_tokens, + setattr( + model_response, + "usage", + Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + ), ) return model_response @@ -525,7 +547,7 @@ class IBMWatsonXAI(BaseLLM): logging_obj: Any, stream: bool = False, input: Optional[Any] = None, - timeout: float = None, + timeout: Optional[float] = None, ): request_str = ( f"response = {request_params['method']}(\n" @@ -535,14 +557,14 @@ class IBMWatsonXAI(BaseLLM): ) logging_obj.pre_call( input=input, - api_key=request_params['headers'].get("Authorization"), + api_key=request_params["headers"].get("Authorization"), additional_args={ - "complete_input_dict": request_params['json'], + "complete_input_dict": request_params["json"], "request_str": request_str, }, ) if timeout: - request_params['timeout'] = timeout + request_params["timeout"] = timeout try: if stream: resp = requests.request( @@ -560,10 +582,10 @@ class IBMWatsonXAI(BaseLLM): if not stream: logging_obj.post_call( input=input, - api_key=request_params['headers'].get("Authorization"), + api_key=request_params["headers"].get("Authorization"), original_response=json.dumps(resp.json()), additional_args={ "status_code": resp.status_code, - "complete_input_dict": request_params['json'], + "complete_input_dict": request_params["json"], }, ) diff --git a/litellm/main.py b/litellm/main.py index 41794ccd50f..454f7f7169a 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1872,7 +1872,7 @@ def completion( model_response=model_response, print_verbose=print_verbose, optional_params=optional_params, - litellm_params=litellm_params, + litellm_params=litellm_params, # type: ignore logger_fn=logger_fn, encoding=encoding, logging_obj=logging, From b2111a97e2ca727a5e11943ae7391387ae0e615d Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 10:51:17 -0700 Subject: [PATCH 35/62] fix use redact_message_input_output_from_logging --- litellm/utils.py | 90 +++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index 4fe23266656..8c064bccae1 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -1212,7 +1212,6 @@ class Logging: print_verbose( f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while logging {traceback.format_exc()}" ) - # Input Integration Logging -> If you want to log the fact that an attempt to call the model was made callbacks = litellm.input_callback + self.dynamic_input_callbacks for callback in callbacks: @@ -1229,29 +1228,17 @@ class Logging: litellm_call_id=self.litellm_params["litellm_call_id"], print_verbose=print_verbose, ) - - elif callback == "lite_debugger": - print_verbose( - f"reaches litedebugger for logging! - model_call_details {self.model_call_details}" - ) - model = self.model_call_details["model"] - messages = self.model_call_details["input"] - print_verbose(f"liteDebuggerClient: {liteDebuggerClient}") - liteDebuggerClient.input_log_event( - model=model, - messages=messages, - end_user=self.model_call_details.get("user", "default"), - litellm_call_id=self.litellm_params["litellm_call_id"], - litellm_params=self.model_call_details["litellm_params"], - optional_params=self.model_call_details["optional_params"], - print_verbose=print_verbose, - call_type=self.call_type, - ) elif callback == "sentry" and add_breadcrumb: - print_verbose("reaches sentry breadcrumbing") + details_to_log = copy.deepcopy(self.model_call_details) + if litellm.turn_off_message_logging: + # make a copy of the _model_Call_details and log it + details_to_log.pop("messages", None) + details_to_log.pop("input", None) + details_to_log.pop("prompt", None) + add_breadcrumb( category="litellm.llm_call", - message=f"Model Call Details pre-call: {self.model_call_details}", + message=f"Model Call Details pre-call: {details_to_log}", level="info", ) elif isinstance(callback, CustomLogger): # custom logger class @@ -1315,7 +1302,7 @@ class Logging: print_verbose( f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while logging {traceback.format_exc()}" ) - + self.redact_message_input_output_from_logging(result=original_response) # Input Integration Logging -> If you want to log the fact that an attempt to call the model was made callbacks = litellm.input_callback + self.dynamic_input_callbacks @@ -1527,31 +1514,7 @@ class Logging: else: callbacks = litellm.success_callback - # check if user opted out of logging message/response to callbacks - if litellm.turn_off_message_logging == True: - # remove messages, prompts, input, response from logging - self.model_call_details["messages"] = "redacted-by-litellm" - self.model_call_details["prompt"] = "" - self.model_call_details["input"] = "" - - # response cleaning - # ChatCompletion Responses - if ( - self.stream - and "complete_streaming_response" in self.model_call_details - ): - _streaming_response = self.model_call_details[ - "complete_streaming_response" - ] - for choice in _streaming_response.choices: - choice.message.content = "redacted-by-litellm" - else: - for choice in result.choices: - choice.message.content = "redacted-by-litellm" - - # Embedding Responses - - # Text Completion Responses + self.redact_message_input_output_from_logging(result=result) for callback in callbacks: try: @@ -2097,6 +2060,9 @@ class Logging: callbacks.append(callback) else: callbacks = litellm._async_success_callback + + self.redact_message_input_output_from_logging(result=result) + print_verbose(f"Async success callbacks: {callbacks}") for callback in callbacks: # check if callback can run for this request @@ -2258,7 +2224,10 @@ class Logging: start_time=start_time, end_time=end_time, ) + result = None # result sent to all loggers, init this to None incase it's not created + + self.redact_message_input_output_from_logging(result=result) for callback in litellm.failure_callback: try: if callback == "lite_debugger": @@ -2443,6 +2412,33 @@ class Logging: f"LiteLLM.LoggingError: [Non-Blocking] Exception occurred while success logging {traceback.format_exc()}" ) + def redact_message_input_output_from_logging(self, result): + """ + Removes messages, prompts, input, response from logging. This modifies the data in-place + only redacts when litellm.turn_off_message_logging == True + """ + # check if user opted out of logging message/response to callbacks + if litellm.turn_off_message_logging == True: + # remove messages, prompts, input, response from logging + self.model_call_details["messages"] = "redacted-by-litellm" + self.model_call_details["prompt"] = "" + self.model_call_details["input"] = "" + + # response cleaning + # ChatCompletion Responses + if self.stream and "complete_streaming_response" in self.model_call_details: + _streaming_response = self.model_call_details[ + "complete_streaming_response" + ] + for choice in _streaming_response.choices: + choice.message.content = "redacted-by-litellm" + else: + if result is not None: + if isinstance(result, litellm.ModelResponse): + if hasattr(result, "choices"): + for choice in result.choices: + choice.message.content = "redacted-by-litellm" + def exception_logging( additional_args={}, From 48f19cf8394a1039ea91d35ff7eb8b785ab6cd44 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 11:06:18 -0700 Subject: [PATCH 36/62] feat(utils.py): unify common auth params across azure/vertex_ai/bedrock/watsonx --- docs/my-website/docs/set_keys.md | 21 +++++++++ litellm/__init__.py | 6 +++ litellm/llms/azure.py | 9 ++++ litellm/llms/bedrock.py | 18 ++++++++ litellm/llms/vertex_ai.py | 16 ++++++- litellm/llms/watsonx.py | 18 ++++++++ litellm/tests/test_completion.py | 50 ++++++++++++++++++++- litellm/utils.py | 76 ++++++++++++++++++++++++-------- 8 files changed, 194 insertions(+), 20 deletions(-) diff --git a/docs/my-website/docs/set_keys.md b/docs/my-website/docs/set_keys.md index 4c8cc42fe79..7686bf70493 100644 --- a/docs/my-website/docs/set_keys.md +++ b/docs/my-website/docs/set_keys.md @@ -5,6 +5,9 @@ LiteLLM allows you to specify the following: * API Base * API Version * API Type +* Project +* Location +* Token Useful Helper functions: * [`check_valid_key()`](#check_valid_key) @@ -43,6 +46,24 @@ os.environ['AZURE_API_TYPE'] = "azure" # [OPTIONAL] os.environ['OPENAI_API_BASE'] = "https://openai-gpt-4-test2-v-12.openai.azure.com/" ``` +### Setting Project, Location, Token + +For cloud providers: +- Azure +- Bedrock +- GCP +- Watson AI + +you might need to set additional parameters. LiteLLM provides a common set of params, that we map across all providers. + +| | LiteLLM param | Watson | Vertex AI | Azure | Bedrock | +|------|--------------|--------------|--------------|--------------|--------------| +| Project | project | watsonx_project | vertex_project | n/a | n/a | +| Region | region_name | watsonx_region_name | vertex_location | n/a | aws_region_name | +| Token | token | watsonx_token or token | n/a | azure_ad_token | n/a | + +If you want, you can call them by their provider-specific params as well. + ## litellm variables ### litellm.api_key diff --git a/litellm/__init__.py b/litellm/__init__.py index 5f23ae33e0b..6a89506c9ca 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -58,6 +58,7 @@ max_tokens = 256 # OpenAI Defaults drop_params = False modify_params = False retry = True +### AUTH ### api_key: Optional[str] = None openai_key: Optional[str] = None azure_key: Optional[str] = None @@ -76,6 +77,10 @@ cloudflare_api_key: Optional[str] = None baseten_key: Optional[str] = None aleph_alpha_key: Optional[str] = None nlp_cloud_key: Optional[str] = None +common_cloud_provider_auth_params: dict = { + "params": ["project", "region_name", "token"], + "providers": ["vertex_ai", "bedrock", "watsonx", "azure"], +} use_client: bool = False ssl_verify: bool = True disable_streaming_logging: bool = False @@ -654,6 +659,7 @@ from .llms.bedrock import ( AmazonLlamaConfig, AmazonStabilityConfig, AmazonMistralConfig, + AmazonBedrockGlobalConfig, ) from .llms.openai import OpenAIConfig, OpenAITextCompletionConfig from .llms.azure import AzureOpenAIConfig, AzureOpenAIError diff --git a/litellm/llms/azure.py b/litellm/llms/azure.py index 7f268c25a59..0fe5c4e7e5d 100644 --- a/litellm/llms/azure.py +++ b/litellm/llms/azure.py @@ -96,6 +96,15 @@ class AzureOpenAIConfig(OpenAIConfig): top_p, ) + def get_mapped_special_auth_params(self) -> dict: + return {"token": "azure_ad_token"} + + def map_special_auth_params(self, non_default_params: dict, optional_params: dict): + for param, value in non_default_params.items(): + if param == "token": + optional_params["azure_ad_token"] = value + return optional_params + def select_azure_base_url_or_endpoint(azure_client_params: dict): # azure_client_params = { diff --git a/litellm/llms/bedrock.py b/litellm/llms/bedrock.py index 149b6847245..894114559d0 100644 --- a/litellm/llms/bedrock.py +++ b/litellm/llms/bedrock.py @@ -29,6 +29,24 @@ class BedrockError(Exception): ) # Call the base class constructor with the parameters it needs +class AmazonBedrockGlobalConfig: + def __init__(self): + pass + + def get_mapped_special_auth_params(self) -> dict: + """ + Mapping of common auth params across bedrock/vertex/azure/watsonx + """ + return {"region_name": "aws_region_name"} + + def map_special_auth_params(self, non_default_params: dict, optional_params: dict): + mapped_params = self.get_mapped_special_auth_params() + for param, value in non_default_params.items(): + if param in mapped_params: + optional_params[mapped_params[param]] = value + return optional_params + + class AmazonTitanConfig: """ Reference: https://us-west-2.console.aws.amazon.com/bedrock/home?region=us-west-2#/providers?model=titan-text-express-v1 diff --git a/litellm/llms/vertex_ai.py b/litellm/llms/vertex_ai.py index e8e9ca5827c..1dbb1cc7a39 100644 --- a/litellm/llms/vertex_ai.py +++ b/litellm/llms/vertex_ai.py @@ -184,6 +184,20 @@ class VertexAIConfig: pass return optional_params + def get_mapped_special_auth_params(self) -> dict: + """ + Common auth params across bedrock/vertex_ai/azure/watsonx + """ + return {"project": "vertex_project", "region_name": "vertex_location"} + + def map_special_auth_params(self, non_default_params: dict, optional_params: dict): + mapped_params = self.get_mapped_special_auth_params() + + for param, value in non_default_params.items(): + if param in mapped_params: + optional_params[mapped_params[param]] = value + return optional_params + import asyncio @@ -529,7 +543,7 @@ def completion( "instances": instances, "vertex_location": vertex_location, "vertex_project": vertex_project, - "safety_settings":safety_settings, + "safety_settings": safety_settings, **optional_params, } if optional_params.get("stream", False) is True: diff --git a/litellm/llms/watsonx.py b/litellm/llms/watsonx.py index 28061919e6a..ac38a2a8fed 100644 --- a/litellm/llms/watsonx.py +++ b/litellm/llms/watsonx.py @@ -131,6 +131,24 @@ class IBMWatsonXAIConfig: "stream", # equivalent to stream ] + def get_mapped_special_auth_params(self) -> dict: + """ + Common auth params across bedrock/vertex_ai/azure/watsonx + """ + return { + "project": "watsonx_project", + "region_name": "watsonx_region_name", + "token": "watsonx_token", + } + + def map_special_auth_params(self, non_default_params: dict, optional_params: dict): + mapped_params = self.get_mapped_special_auth_params() + + for param, value in non_default_params.items(): + if param in mapped_params: + optional_params[mapped_params[param]] = value + return optional_params + def convert_messages_to_prompt(model, messages, provider, custom_prompt_dict): # handle anthropic prompts and amazon titan prompts diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 5317108412d..14be9592b83 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -2654,6 +2654,7 @@ def test_completion_palm_stream(): except Exception as e: pytest.fail(f"Error occurred: {e}") + def test_completion_watsonx(): litellm.set_verbose = True model_name = "watsonx/ibm/granite-13b-chat-v2" @@ -2671,10 +2672,57 @@ def test_completion_watsonx(): except Exception as e: pytest.fail(f"Error occurred: {e}") + +@pytest.mark.parametrize( + "provider, model, project, region_name, token", + [ + ("azure", "chatgpt-v-2", None, None, "test-token"), + ("vertex_ai", "anthropic-claude-3", "adroit-crow-1", "us-east1", None), + ("watsonx", "ibm/granite", "96946574", "dallas", "1234"), + ("bedrock", "anthropic.claude-3", None, "us-east-1", None), + ], +) +def test_unified_auth_params(provider, model, project, region_name, token): + """ + Check if params = ["project", "region_name", "token"] + are correctly translated for = ["azure", "vertex_ai", "watsonx", "aws"] + + tests get_optional_params + """ + data = { + "project": project, + "region_name": region_name, + "token": token, + "custom_llm_provider": provider, + "model": model, + } + + translated_optional_params = litellm.utils.get_optional_params(**data) + + if provider == "azure": + special_auth_params = ( + litellm.AzureOpenAIConfig().get_mapped_special_auth_params() + ) + elif provider == "bedrock": + special_auth_params = ( + litellm.AmazonBedrockGlobalConfig().get_mapped_special_auth_params() + ) + elif provider == "vertex_ai": + special_auth_params = litellm.VertexAIConfig().get_mapped_special_auth_params() + elif provider == "watsonx": + special_auth_params = ( + litellm.IBMWatsonXAIConfig().get_mapped_special_auth_params() + ) + + for param, value in special_auth_params.items(): + assert param in data + assert value in translated_optional_params + + @pytest.mark.asyncio async def test_acompletion_watsonx(): litellm.set_verbose = True - model_name = "watsonx/deployment/"+os.getenv("WATSONX_DEPLOYMENT_ID") + model_name = "watsonx/deployment/" + os.getenv("WATSONX_DEPLOYMENT_ID") print("testing watsonx") try: response = await litellm.acompletion( diff --git a/litellm/utils.py b/litellm/utils.py index 9f176c19490..ccd6bd3dcb6 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -4619,7 +4619,36 @@ def get_optional_params( k.startswith("vertex_") and custom_llm_provider != "vertex_ai" ): # allow dynamically setting vertex ai init logic continue + passed_params[k] = v + + optional_params = {} + + common_auth_dict = litellm.common_cloud_provider_auth_params + if custom_llm_provider in common_auth_dict["providers"]: + """ + Check if params = ["project", "region_name", "token"] + and correctly translate for = ["azure", "vertex_ai", "watsonx", "aws"] + """ + if custom_llm_provider == "azure": + optional_params = litellm.AzureOpenAIConfig().map_special_auth_params( + non_default_params=passed_params, optional_params=optional_params + ) + elif custom_llm_provider == "bedrock": + optional_params = ( + litellm.AmazonBedrockGlobalConfig().map_special_auth_params( + non_default_params=passed_params, optional_params=optional_params + ) + ) + elif custom_llm_provider == "vertex_ai": + optional_params = litellm.VertexAIConfig().map_special_auth_params( + non_default_params=passed_params, optional_params=optional_params + ) + elif custom_llm_provider == "watsonx": + optional_params = litellm.IBMWatsonXAIConfig().map_special_auth_params( + non_default_params=passed_params, optional_params=optional_params + ) + default_params = { "functions": None, "function_call": None, @@ -4655,7 +4684,7 @@ def get_optional_params( and v != default_params[k] ) } - optional_params = {} + ## raise exception if function calling passed in for a provider that doesn't support it if ( "functions" in non_default_params @@ -5446,17 +5475,21 @@ def get_optional_params( optional_params["random_seed"] = seed if stop is not None: optional_params["stop_sequences"] = stop - + # WatsonX-only parameters extra_body = {} if "decoding_method" in passed_params: extra_body["decoding_method"] = passed_params.pop("decoding_method") - if "min_tokens" in passed_params or "min_new_tokens" in passed_params: - extra_body["min_new_tokens"] = passed_params.pop("min_tokens", passed_params.pop("min_new_tokens")) + if "min_tokens" in passed_params or "min_new_tokens" in passed_params: + extra_body["min_new_tokens"] = passed_params.pop( + "min_tokens", passed_params.pop("min_new_tokens") + ) if "top_k" in passed_params: extra_body["top_k"] = passed_params.pop("top_k") if "truncate_input_tokens" in passed_params: - extra_body["truncate_input_tokens"] = passed_params.pop("truncate_input_tokens") + extra_body["truncate_input_tokens"] = passed_params.pop( + "truncate_input_tokens" + ) if "length_penalty" in passed_params: extra_body["length_penalty"] = passed_params.pop("length_penalty") if "time_limit" in passed_params: @@ -5464,7 +5497,7 @@ def get_optional_params( if "return_options" in passed_params: extra_body["return_options"] = passed_params.pop("return_options") optional_params["extra_body"] = ( - extra_body # openai client supports `extra_body` param + extra_body # openai client supports `extra_body` param ) else: # assume passing in params for openai/azure openai print_verbose( @@ -9793,7 +9826,7 @@ class CustomStreamWrapper: "is_finished": chunk["is_finished"], "finish_reason": finish_reason, } - + def handle_watsonx_stream(self, chunk): try: if isinstance(chunk, dict): @@ -9801,19 +9834,21 @@ class CustomStreamWrapper: elif isinstance(chunk, (str, bytes)): if isinstance(chunk, bytes): chunk = chunk.decode("utf-8") - if 'generated_text' in chunk: - response = chunk.replace('data: ', '').strip() + if "generated_text" in chunk: + response = chunk.replace("data: ", "").strip() parsed_response = json.loads(response) else: return {"text": "", "is_finished": False} else: print_verbose(f"chunk: {chunk} (Type: {type(chunk)})") - raise ValueError(f"Unable to parse response. Original response: {chunk}") + raise ValueError( + f"Unable to parse response. Original response: {chunk}" + ) results = parsed_response.get("results", []) if len(results) > 0: text = results[0].get("generated_text", "") finish_reason = results[0].get("stop_reason") - is_finished = finish_reason != 'not_finished' + is_finished = finish_reason != "not_finished" return { "text": text, "is_finished": is_finished, @@ -10085,14 +10120,19 @@ class CustomStreamWrapper: completion_obj["content"] = response_obj["text"] print_verbose(f"completion obj content: {completion_obj['content']}") if response_obj.get("prompt_tokens") is not None: - prompt_token_count = getattr(model_response.usage, "prompt_tokens", 0) - model_response.usage.prompt_tokens = (prompt_token_count+response_obj["prompt_tokens"]) + prompt_token_count = getattr( + model_response.usage, "prompt_tokens", 0 + ) + model_response.usage.prompt_tokens = ( + prompt_token_count + response_obj["prompt_tokens"] + ) if response_obj.get("completion_tokens") is not None: - model_response.usage.completion_tokens = response_obj["completion_tokens"] - model_response.usage.total_tokens = ( - getattr(model_response.usage, "prompt_tokens", 0) - + getattr(model_response.usage, "completion_tokens", 0) - ) + model_response.usage.completion_tokens = response_obj[ + "completion_tokens" + ] + model_response.usage.total_tokens = getattr( + model_response.usage, "prompt_tokens", 0 + ) + getattr(model_response.usage, "completion_tokens", 0) if response_obj["is_finished"]: self.received_finish_reason = response_obj["finish_reason"] elif self.custom_llm_provider == "text-completion-openai": From 2c67791663c55fea855011c9cfbbff8d4cd407fd Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 11:19:00 -0700 Subject: [PATCH 37/62] test(test_completion.py): modify acompletion test to call pre-deployed watsonx endpoint --- litellm/tests/test_completion.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 5317108412d..d1047d3d66e 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -2654,6 +2654,7 @@ def test_completion_palm_stream(): except Exception as e: pytest.fail(f"Error occurred: {e}") + def test_completion_watsonx(): litellm.set_verbose = True model_name = "watsonx/ibm/granite-13b-chat-v2" @@ -2671,10 +2672,11 @@ def test_completion_watsonx(): except Exception as e: pytest.fail(f"Error occurred: {e}") + @pytest.mark.asyncio async def test_acompletion_watsonx(): litellm.set_verbose = True - model_name = "watsonx/deployment/"+os.getenv("WATSONX_DEPLOYMENT_ID") + model_name = "watsonx/ibm/granite-13b-chat-v2" print("testing watsonx") try: response = await litellm.acompletion( @@ -2682,7 +2684,6 @@ async def test_acompletion_watsonx(): messages=messages, temperature=0.2, max_tokens=80, - space_id=os.getenv("WATSONX_SPACE_ID_TEST"), ) # Add any assertions here to check the response print(response) From 4ce27e121934ad0b062dbe20576a8d7181baacd3 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 11:23:08 -0700 Subject: [PATCH 38/62] fix - sentry data redaction --- litellm/utils.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index 8c064bccae1..984212b4359 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -1320,9 +1320,17 @@ class Logging: ) elif callback == "sentry" and add_breadcrumb: print_verbose("reaches sentry breadcrumbing") + + details_to_log = copy.deepcopy(self.model_call_details) + if litellm.turn_off_message_logging: + # make a copy of the _model_Call_details and log it + details_to_log.pop("messages", None) + details_to_log.pop("input", None) + details_to_log.pop("prompt", None) + add_breadcrumb( category="litellm.llm_call", - message=f"Model Call Details post-call: {self.model_call_details}", + message=f"Model Call Details post-call: {details_to_log}", level="info", ) elif isinstance(callback, CustomLogger): # custom logger class @@ -2620,9 +2628,15 @@ def function_setup( dynamic_success_callbacks = kwargs.pop("success_callback") if add_breadcrumb: + details_to_log = copy.deepcopy(kwargs) + if litellm.turn_off_message_logging: + # make a copy of the _model_Call_details and log it + details_to_log.pop("messages", None) + details_to_log.pop("input", None) + details_to_log.pop("prompt", None) add_breadcrumb( category="litellm.llm_call", - message=f"Positional Args: {args}, Keyword Args: {kwargs}", + message=f"Positional Args: {args}, Keyword Args: {details_to_log}", level="info", ) if "logger_fn" in kwargs: From 01478c9148bfa02b6c520825478b7f18dd68d1c6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 11:23:35 -0700 Subject: [PATCH 39/62] docs - langfuse redact messages --- docs/my-website/docs/observability/langfuse_integration.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/my-website/docs/observability/langfuse_integration.md b/docs/my-website/docs/observability/langfuse_integration.md index 50b016d09f9..fe210e6b710 100644 --- a/docs/my-website/docs/observability/langfuse_integration.md +++ b/docs/my-website/docs/observability/langfuse_integration.md @@ -167,6 +167,9 @@ messages = [ chat(messages) ``` +## Redacting Messages, Response Content from Langfuse Logging + +Set `litellm.turn_off_message_logging=True` This will prevent the messages and responses from being logged to langfuse, but request metadata will still be logged. ## Troubleshooting & Errors ### Data not getting logged to Langfuse ? From f55838d1853d0773c7acfbdaf173f3de1922955e Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 11:23:46 -0700 Subject: [PATCH 40/62] sentry redact messages --- docs/my-website/docs/observability/sentry.md | 4 ++++ docs/my-website/docs/proxy/logging.md | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/docs/my-website/docs/observability/sentry.md b/docs/my-website/docs/observability/sentry.md index 255dd55cfec..5877db6610d 100644 --- a/docs/my-website/docs/observability/sentry.md +++ b/docs/my-website/docs/observability/sentry.md @@ -40,5 +40,9 @@ response = completion(model="gpt-3.5-turbo", messages=[{"role": "user", "content print(response) ``` +## Redacting Messages, Response Content from Sentry Logging + +Set `litellm.turn_off_message_logging=True` This will prevent the messages and responses from being logged to sentry, but request metadata will still be logged. + [Let us know](https://github.com/BerriAI/litellm/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.yml&title=%5BFeature%5D%3A+) if you need any additional options from Sentry. diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md index 48a5955b177..1a5a7f0f077 100644 --- a/docs/my-website/docs/proxy/logging.md +++ b/docs/my-website/docs/proxy/logging.md @@ -569,6 +569,22 @@ curl -X POST 'http://0.0.0.0:4000/key/generate' \ All requests made with these keys will log data to their team-specific logging. +### Redacting Messages, Response Content from Langfuse Logging + +Set `litellm.turn_off_message_logging=True` This will prevent the messages and responses from being logged to langfuse, but request metadata will still be logged. + +```yaml +model_list: + - model_name: gpt-3.5-turbo + litellm_params: + model: gpt-3.5-turbo +litellm_settings: + success_callback: ["langfuse"] + turn_off_message_logging: True +``` + + + ## Logging Proxy Input/Output - DataDog We will use the `--config` to set `litellm.success_callback = ["datadog"]` this will log all successfull LLM calls to DataDog From 463b1aff24665e2235045c4aca32615f6dc51472 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 11:43:00 -0700 Subject: [PATCH 41/62] fix(vertex_ai.py): support safety settings for async streaming calls --- litellm/llms/vertex_ai.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/llms/vertex_ai.py b/litellm/llms/vertex_ai.py index e8e9ca5827c..f107af38be3 100644 --- a/litellm/llms/vertex_ai.py +++ b/litellm/llms/vertex_ai.py @@ -529,7 +529,7 @@ def completion( "instances": instances, "vertex_location": vertex_location, "vertex_project": vertex_project, - "safety_settings":safety_settings, + "safety_settings": safety_settings, **optional_params, } if optional_params.get("stream", False) is True: @@ -1025,6 +1025,7 @@ async def async_streaming( instances=None, vertex_project=None, vertex_location=None, + safety_settings=None, **optional_params, ): """ @@ -1051,6 +1052,7 @@ async def async_streaming( response = await llm_model._generate_content_streaming_async( contents=content, generation_config=optional_params, + safety_settings=safety_settings, tools=tools, ) From e49fe47d2ea25fea052635e16231a3db5720accf Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 14:11:00 -0700 Subject: [PATCH 42/62] fix - only run global_proxy_spend on chat completion calls --- litellm/proxy/auth/auth_checks.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/auth/auth_checks.py b/litellm/proxy/auth/auth_checks.py index c037190d352..a393ec90a09 100644 --- a/litellm/proxy/auth/auth_checks.py +++ b/litellm/proxy/auth/auth_checks.py @@ -95,7 +95,15 @@ def common_checks( f"'user' param not passed in. 'enforce_user_param'={general_settings['enforce_user_param']}" ) # 7. [OPTIONAL] If 'litellm.max_budget' is set (>0), is proxy under budget - if litellm.max_budget > 0 and global_proxy_spend is not None: + if ( + litellm.max_budget > 0 + 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 route in LiteLLMRoutes.openai_routes.value + and route != "/v1/models" + and route != "/models" + ): if global_proxy_spend > litellm.max_budget: raise Exception( f"ExceededBudget: LiteLLM Proxy has exceeded its budget. Current spend: {global_proxy_spend}; Max Budget: {litellm.max_budget}" From 5f0f3f9fe32dce97219f000ccdbbaa2c27717de1 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 14:13:34 -0700 Subject: [PATCH 43/62] fix(utils.py): don't return usage for streaming - openai spec --- litellm/utils.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/litellm/utils.py b/litellm/utils.py index 0cf4f9f1650..045506b228d 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5482,17 +5482,21 @@ def get_optional_params( optional_params["random_seed"] = seed if stop is not None: optional_params["stop_sequences"] = stop - + # WatsonX-only parameters extra_body = {} if "decoding_method" in passed_params: extra_body["decoding_method"] = passed_params.pop("decoding_method") - if "min_tokens" in passed_params or "min_new_tokens" in passed_params: - extra_body["min_new_tokens"] = passed_params.pop("min_tokens", passed_params.pop("min_new_tokens")) + if "min_tokens" in passed_params or "min_new_tokens" in passed_params: + extra_body["min_new_tokens"] = passed_params.pop( + "min_tokens", passed_params.pop("min_new_tokens") + ) if "top_k" in passed_params: extra_body["top_k"] = passed_params.pop("top_k") if "truncate_input_tokens" in passed_params: - extra_body["truncate_input_tokens"] = passed_params.pop("truncate_input_tokens") + extra_body["truncate_input_tokens"] = passed_params.pop( + "truncate_input_tokens" + ) if "length_penalty" in passed_params: extra_body["length_penalty"] = passed_params.pop("length_penalty") if "time_limit" in passed_params: @@ -5500,7 +5504,7 @@ def get_optional_params( if "return_options" in passed_params: extra_body["return_options"] = passed_params.pop("return_options") optional_params["extra_body"] = ( - extra_body # openai client supports `extra_body` param + extra_body # openai client supports `extra_body` param ) else: # assume passing in params for openai/azure openai print_verbose( @@ -9829,7 +9833,7 @@ class CustomStreamWrapper: "is_finished": chunk["is_finished"], "finish_reason": finish_reason, } - + def handle_watsonx_stream(self, chunk): try: if isinstance(chunk, dict): @@ -9837,19 +9841,21 @@ class CustomStreamWrapper: elif isinstance(chunk, (str, bytes)): if isinstance(chunk, bytes): chunk = chunk.decode("utf-8") - if 'generated_text' in chunk: - response = chunk.replace('data: ', '').strip() + if "generated_text" in chunk: + response = chunk.replace("data: ", "").strip() parsed_response = json.loads(response) else: return {"text": "", "is_finished": False} else: print_verbose(f"chunk: {chunk} (Type: {type(chunk)})") - raise ValueError(f"Unable to parse response. Original response: {chunk}") + raise ValueError( + f"Unable to parse response. Original response: {chunk}" + ) results = parsed_response.get("results", []) if len(results) > 0: text = results[0].get("generated_text", "") finish_reason = results[0].get("stop_reason") - is_finished = finish_reason != 'not_finished' + is_finished = finish_reason != "not_finished" return { "text": text, "is_finished": is_finished, @@ -10119,16 +10125,6 @@ class CustomStreamWrapper: elif self.custom_llm_provider == "watsonx": response_obj = self.handle_watsonx_stream(chunk) completion_obj["content"] = response_obj["text"] - print_verbose(f"completion obj content: {completion_obj['content']}") - if response_obj.get("prompt_tokens") is not None: - prompt_token_count = getattr(model_response.usage, "prompt_tokens", 0) - model_response.usage.prompt_tokens = (prompt_token_count+response_obj["prompt_tokens"]) - if response_obj.get("completion_tokens") is not None: - model_response.usage.completion_tokens = response_obj["completion_tokens"] - model_response.usage.total_tokens = ( - getattr(model_response.usage, "prompt_tokens", 0) - + getattr(model_response.usage, "completion_tokens", 0) - ) if response_obj["is_finished"]: self.received_finish_reason = response_obj["finish_reason"] elif self.custom_llm_provider == "text-completion-openai": From 5e0bd5982ebb32eff9e95fa91d270fbb13b1e456 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 14:48:07 -0700 Subject: [PATCH 44/62] fix(router.py): fix sync should_retry logic --- litellm/router.py | 71 ++++++++++++++++---------- litellm/tests/test_router_init.py | 85 ++++++++++++++++++++++--------- 2 files changed, 103 insertions(+), 53 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index be20f5d2bfa..161e00b16d1 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1453,6 +1453,7 @@ class Router: await asyncio.sleep(timeout) elif RouterErrors.user_defined_ratelimit_error.value in str(e): raise e # don't wait to retry if deployment hits user-defined rate-limit + elif hasattr(original_exception, "status_code") and litellm._should_retry( status_code=original_exception.status_code ): @@ -1614,6 +1615,38 @@ class Router: raise e raise original_exception + def _router_should_retry( + self, e: Exception, remaining_retries: int, num_retries: int + ): + if "No models available" in str(e): + timeout = litellm._calculate_retry_after( + remaining_retries=remaining_retries, + max_retries=num_retries, + min_timeout=self.retry_after, + ) + time.sleep(timeout) + elif ( + hasattr(e, "status_code") + and hasattr(e, "response") + and litellm._should_retry(status_code=e.status_code) + ): + if hasattr(e.response, "headers"): + timeout = litellm._calculate_retry_after( + remaining_retries=remaining_retries, + max_retries=num_retries, + response_headers=e.response.headers, + min_timeout=self.retry_after, + ) + else: + timeout = litellm._calculate_retry_after( + remaining_retries=remaining_retries, + max_retries=num_retries, + min_timeout=self.retry_after, + ) + time.sleep(timeout) + else: + raise e + def function_with_retries(self, *args, **kwargs): """ Try calling the model 3 times. Shuffle between available deployments. @@ -1649,6 +1682,11 @@ class Router: if num_retries > 0: kwargs = self.log_retry(kwargs=kwargs, e=original_exception) ### RETRY + self._router_should_retry( + e=original_exception, + remaining_retries=num_retries, + num_retries=num_retries, + ) for current_attempt in range(num_retries): verbose_router_logger.debug( f"retrying request. Current attempt - {current_attempt}; retries left: {num_retries}" @@ -1662,34 +1700,11 @@ class Router: ## LOGGING kwargs = self.log_retry(kwargs=kwargs, e=e) remaining_retries = num_retries - current_attempt - if "No models available" in str(e): - timeout = litellm._calculate_retry_after( - remaining_retries=remaining_retries, - max_retries=num_retries, - min_timeout=self.retry_after, - ) - time.sleep(timeout) - elif ( - hasattr(e, "status_code") - and hasattr(e, "response") - and litellm._should_retry(status_code=e.status_code) - ): - if hasattr(e.response, "headers"): - timeout = litellm._calculate_retry_after( - remaining_retries=remaining_retries, - max_retries=num_retries, - response_headers=e.response.headers, - min_timeout=self.retry_after, - ) - else: - timeout = litellm._calculate_retry_after( - remaining_retries=remaining_retries, - max_retries=num_retries, - min_timeout=self.retry_after, - ) - time.sleep(timeout) - else: - raise e + self._router_should_retry( + e=e, + remaining_retries=remaining_retries, + num_retries=num_retries, + ) raise original_exception ### HELPER FUNCTIONS diff --git a/litellm/tests/test_router_init.py b/litellm/tests/test_router_init.py index 862d7e96581..13f7bd190c3 100644 --- a/litellm/tests/test_router_init.py +++ b/litellm/tests/test_router_init.py @@ -396,7 +396,9 @@ def test_router_init_gpt_4_vision_enhancements(): pytest.fail(f"Error occurred: {e}") -def test_openai_with_organization(): +@pytest.mark.parametrize("sync_mode", [True, False]) +@pytest.mark.asyncio +async def test_openai_with_organization(sync_mode): try: print("Testing OpenAI with organization") model_list = [ @@ -418,32 +420,65 @@ def test_openai_with_organization(): print(router.model_list) print(router.model_list[0]) - openai_client = router._get_client( - deployment=router.model_list[0], - kwargs={"input": ["hello"], "model": "openai-bad-org"}, - ) - print(vars(openai_client)) - - assert openai_client.organization == "org-ikDc4ex8NB" - - # bad org raises error - - try: - response = router.completion( - model="openai-bad-org", - messages=[{"role": "user", "content": "this is a test"}], + if sync_mode: + openai_client = router._get_client( + deployment=router.model_list[0], + kwargs={"input": ["hello"], "model": "openai-bad-org"}, ) - pytest.fail("Request should have failed - This organization does not exist") - except Exception as e: - print("Got exception: " + str(e)) - assert "No such organization: org-ikDc4ex8NB" in str(e) + print(vars(openai_client)) - # good org works - response = router.completion( - model="openai-good-org", - messages=[{"role": "user", "content": "this is a test"}], - max_tokens=5, - ) + assert openai_client.organization == "org-ikDc4ex8NB" + + # bad org raises error + + try: + response = router.completion( + model="openai-bad-org", + messages=[{"role": "user", "content": "this is a test"}], + ) + pytest.fail( + "Request should have failed - This organization does not exist" + ) + except Exception as e: + print("Got exception: " + str(e)) + assert "No such organization: org-ikDc4ex8NB" in str(e) + + # good org works + response = router.completion( + model="openai-good-org", + messages=[{"role": "user", "content": "this is a test"}], + max_tokens=5, + ) + else: + openai_client = router._get_client( + deployment=router.model_list[0], + kwargs={"input": ["hello"], "model": "openai-bad-org"}, + client_type="async", + ) + print(vars(openai_client)) + + assert openai_client.organization == "org-ikDc4ex8NB" + + # bad org raises error + + try: + response = await router.acompletion( + model="openai-bad-org", + messages=[{"role": "user", "content": "this is a test"}], + ) + pytest.fail( + "Request should have failed - This organization does not exist" + ) + except Exception as e: + print("Got exception: " + str(e)) + assert "No such organization: org-ikDc4ex8NB" in str(e) + + # good org works + response = await router.acompletion( + model="openai-good-org", + messages=[{"role": "user", "content": "this is a test"}], + max_tokens=5, + ) except Exception as e: pytest.fail(f"Error occurred: {e}") From 1b586e50420bd19fe143331acb1543634e4a7888 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 14:53:18 -0700 Subject: [PATCH 45/62] fix - allow langfuse init with flush interval --- litellm/integrations/langfuse.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/litellm/integrations/langfuse.py b/litellm/integrations/langfuse.py index 51ad4d19abe..c2612feb805 100644 --- a/litellm/integrations/langfuse.py +++ b/litellm/integrations/langfuse.py @@ -12,7 +12,9 @@ import litellm class LangFuseLogger: # Class variables or attributes - def __init__(self, langfuse_public_key=None, langfuse_secret=None): + def __init__( + self, langfuse_public_key=None, langfuse_secret=None, flush_interval=1 + ): try: from langfuse import Langfuse except Exception as e: @@ -31,7 +33,7 @@ class LangFuseLogger: host=self.langfuse_host, release=self.langfuse_release, debug=self.langfuse_debug, - flush_interval=1, # flush interval in seconds + flush_interval=flush_interval, # flush interval in seconds ) # set the current langfuse project id in the environ From 9f24421d44699704b938885fa02d957d0c09ea5a Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 15:13:20 -0700 Subject: [PATCH 46/62] fix(router.py): fix router should_retry --- litellm/router.py | 8 ++-- litellm/tests/test_acooldowns_router.py | 50 +++++++++++++++++-------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index 161e00b16d1..b23efa71d31 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1625,12 +1625,10 @@ class Router: min_timeout=self.retry_after, ) time.sleep(timeout) - elif ( - hasattr(e, "status_code") - and hasattr(e, "response") - and litellm._should_retry(status_code=e.status_code) + elif hasattr(e, "status_code") and litellm._should_retry( + status_code=e.status_code ): - if hasattr(e.response, "headers"): + if hasattr(e, "response") and hasattr(e.response, "headers"): timeout = litellm._calculate_retry_after( remaining_retries=remaining_retries, max_retries=num_retries, diff --git a/litellm/tests/test_acooldowns_router.py b/litellm/tests/test_acooldowns_router.py index 28573d5be20..7dced31a8f3 100644 --- a/litellm/tests/test_acooldowns_router.py +++ b/litellm/tests/test_acooldowns_router.py @@ -119,7 +119,9 @@ def test_multiple_deployments_parallel(): # test_multiple_deployments_parallel() -def test_cooldown_same_model_name(): +@pytest.mark.parametrize("sync_mode", [True, False]) +@pytest.mark.asyncio +async def test_cooldown_same_model_name(sync_mode): # users could have the same model with different api_base # example # azure/chatgpt, api_base: 1234 @@ -161,22 +163,40 @@ def test_cooldown_same_model_name(): num_retries=3, ) # type: ignore - response = router.completion( - model="gpt-3.5-turbo", - messages=[{"role": "user", "content": "hello this request will pass"}], - ) - print(router.model_list) - model_ids = [] - for model in router.model_list: - model_ids.append(model["model_info"]["id"]) - print("\n litellm model ids ", model_ids) + if sync_mode: + response = router.completion( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "hello this request will pass"}], + ) + print(router.model_list) + model_ids = [] + for model in router.model_list: + model_ids.append(model["model_info"]["id"]) + print("\n litellm model ids ", model_ids) - # example litellm_model_names ['azure/chatgpt-v-2-ModelID-64321', 'azure/chatgpt-v-2-ModelID-63960'] - assert ( - model_ids[0] != model_ids[1] - ) # ensure both models have a uuid added, and they have different names + # example litellm_model_names ['azure/chatgpt-v-2-ModelID-64321', 'azure/chatgpt-v-2-ModelID-63960'] + assert ( + model_ids[0] != model_ids[1] + ) # ensure both models have a uuid added, and they have different names - print("\ngot response\n", response) + print("\ngot response\n", response) + else: + response = await router.acompletion( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "hello this request will pass"}], + ) + print(router.model_list) + model_ids = [] + for model in router.model_list: + model_ids.append(model["model_info"]["id"]) + print("\n litellm model ids ", model_ids) + + # example litellm_model_names ['azure/chatgpt-v-2-ModelID-64321', 'azure/chatgpt-v-2-ModelID-63960'] + assert ( + model_ids[0] != model_ids[1] + ) # ensure both models have a uuid added, and they have different names + + print("\ngot response\n", response) except Exception as e: pytest.fail(f"Got unexpected exception on router! - {e}") From d6827c357447430fed7c47bce1741d5b6a9b1f29 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 15:23:18 -0700 Subject: [PATCH 47/62] fix - link to langfuse traces on slack alerts --- litellm/integrations/slack_alerting.py | 115 +++++++++++++++++-------- 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/litellm/integrations/slack_alerting.py b/litellm/integrations/slack_alerting.py index adc56698c39..e3e5811ed5f 100644 --- a/litellm/integrations/slack_alerting.py +++ b/litellm/integrations/slack_alerting.py @@ -12,6 +12,7 @@ from litellm.caching import DualCache import asyncio import aiohttp from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler +import datetime class SlackAlerting: @@ -47,6 +48,18 @@ class SlackAlerting: self.internal_usage_cache = DualCache() self.async_http_handler = AsyncHTTPHandler() self.alert_to_webhook_url = alert_to_webhook_url + self.langfuse_logger = None + + try: + from litellm.integrations.langfuse import LangFuseLogger + + self.langfuse_logger = LangFuseLogger( + os.getenv("LANGFUSE_PUBLIC_KEY"), + os.getenv("LANGFUSE_SECRET_KEY"), + flush_interval=1, + ) + except: + pass pass @@ -93,39 +106,68 @@ class SlackAlerting: request_info: str, request_data: Optional[dict] = None, kwargs: Optional[dict] = None, + type: Literal["hanging_request", "slow_response"] = "hanging_request", + start_time: Optional[datetime.datetime] = None, + end_time: Optional[datetime.datetime] = None, ): import uuid # For now: do nothing as we're debugging why this is not working as expected + if request_data is not None: + trace_id = request_data.get("metadata", {}).get( + "trace_id", None + ) # get langfuse trace id + if trace_id is None: + trace_id = "litellm-alert-trace-" + str(uuid.uuid4()) + request_data["metadata"]["trace_id"] = trace_id + elif kwargs is not None: + _litellm_params = kwargs.get("litellm_params", {}) + trace_id = _litellm_params.get("metadata", {}).get( + "trace_id", None + ) # get langfuse trace id + if trace_id is None: + trace_id = "litellm-alert-trace-" + str(uuid.uuid4()) + _litellm_params["metadata"]["trace_id"] = trace_id + + # Log hanging request as an error on langfuse + if type == "hanging_request": + if self.langfuse_logger is not None: + _logging_kwargs = copy.deepcopy(request_data) + if _logging_kwargs is None: + _logging_kwargs = {} + _logging_kwargs["litellm_params"] = {} + request_data = request_data or {} + _logging_kwargs["litellm_params"]["metadata"] = request_data.get( + "metadata", {} + ) + # log to langfuse in a separate thread + import threading + + threading.Thread( + target=self.langfuse_logger.log_event, + args=( + _logging_kwargs, + None, + start_time, + end_time, + None, + print, + "ERROR", + "Requests is hanging", + ), + ).start() + + _langfuse_host = os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com") + _langfuse_project_id = os.environ.get("LANGFUSE_PROJECT_ID") + + # langfuse urls look like: https://us.cloud.langfuse.com/project/************/traces/litellm-alert-trace-ididi9dk-09292-************ + + _langfuse_url = ( + f"{_langfuse_host}/project/{_langfuse_project_id}/traces/{trace_id}" + ) + request_info += f"\n🪢 Langfuse Trace: {_langfuse_url}" return request_info - # if request_data is not None: - # trace_id = request_data.get("metadata", {}).get( - # "trace_id", None - # ) # get langfuse trace id - # if trace_id is None: - # trace_id = "litellm-alert-trace-" + str(uuid.uuid4()) - # request_data["metadata"]["trace_id"] = trace_id - # elif kwargs is not None: - # _litellm_params = kwargs.get("litellm_params", {}) - # trace_id = _litellm_params.get("metadata", {}).get( - # "trace_id", None - # ) # get langfuse trace id - # if trace_id is None: - # trace_id = "litellm-alert-trace-" + str(uuid.uuid4()) - # _litellm_params["metadata"]["trace_id"] = trace_id - - # _langfuse_host = os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com") - # _langfuse_project_id = os.environ.get("LANGFUSE_PROJECT_ID") - - # # langfuse urls look like: https://us.cloud.langfuse.com/project/************/traces/litellm-alert-trace-ididi9dk-09292-************ - - # _langfuse_url = ( - # f"{_langfuse_host}/project/{_langfuse_project_id}/traces/{trace_id}" - # ) - # request_info += f"\n🪢 Langfuse Trace: {_langfuse_url}" - # return request_info - def _response_taking_too_long_callback( self, kwargs, # kwargs to completion @@ -194,7 +236,7 @@ class SlackAlerting: if time_difference_float > self.alerting_threshold: if "langfuse" in litellm.success_callback: request_info = self._add_langfuse_trace_id_to_alert( - request_info=request_info, kwargs=kwargs + request_info=request_info, kwargs=kwargs, type="slow_response" ) # add deployment latencies to alert if ( @@ -222,8 +264,8 @@ class SlackAlerting: async def response_taking_too_long( self, - start_time: Optional[float] = None, - end_time: Optional[float] = None, + start_time: Optional[datetime.datetime] = None, + end_time: Optional[datetime.datetime] = None, type: Literal["hanging_request", "slow_response"] = "hanging_request", request_data: Optional[dict] = None, ): @@ -243,10 +285,6 @@ class SlackAlerting: except: messages = "" request_info = f"\nRequest Model: `{model}`\nMessages: `{messages}`" - if "langfuse" in litellm.success_callback: - request_info = self._add_langfuse_trace_id_to_alert( - request_info=request_info, request_data=request_data - ) else: request_info = "" @@ -288,6 +326,15 @@ class SlackAlerting: f"`Requests are hanging - {self.alerting_threshold}s+ request time`" ) + if "langfuse" in litellm.success_callback: + request_info = self._add_langfuse_trace_id_to_alert( + request_info=request_info, + request_data=request_data, + type="hanging_request", + start_time=start_time, + end_time=end_time, + ) + # add deployment latencies to alert _deployment_latency_map = self._get_deployment_latencies_to_alert( metadata=request_data.get("metadata", {}) From 3b8126cf51bb530e402b6e26610423cb1f5f67d6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 15:33:21 -0700 Subject: [PATCH 48/62] docs - alerting --- docs/my-website/docs/proxy/alerting.md | 12 ++++++------ docs/my-website/sidebars.js | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/my-website/docs/proxy/alerting.md b/docs/my-website/docs/proxy/alerting.md index feb54babd2e..4275e0bf06f 100644 --- a/docs/my-website/docs/proxy/alerting.md +++ b/docs/my-website/docs/proxy/alerting.md @@ -1,13 +1,13 @@ -# Slack Alerting +# 🚨 Alerting Get alerts for: -- hanging LLM api calls -- failed LLM api calls -- slow LLM api calls -- budget Tracking per key/user: +- Hanging LLM api calls +- Failed LLM api calls +- Slow LLM api calls +- Budget Tracking per key/user: - When a User/Key crosses their Budget - When a User/Key is 15% away from crossing their Budget -- failed db read/writes +- Failed db read/writes ## Quick Start diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index a5d1f30aed2..e4f4e806e1c 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -43,6 +43,12 @@ const sidebars = { "proxy/user_keys", "proxy/enterprise", "proxy/virtual_keys", + "proxy/alerting", + { + type: "category", + label: "Logging", + items: ["proxy/logging", "proxy/streaming_logging"], + }, "proxy/team_based_routing", "proxy/ui", "proxy/cost_tracking", @@ -58,11 +64,6 @@ const sidebars = { "proxy/pii_masking", "proxy/prompt_injection", "proxy/caching", - { - type: "category", - label: "Logging, Alerting", - items: ["proxy/logging", "proxy/alerting", "proxy/streaming_logging"], - }, "proxy/prometheus", "proxy/call_hooks", "proxy/rules", From cc51db199988d49b3e1728344b1d3761c7189b5c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 15:44:30 -0700 Subject: [PATCH 49/62] fix slack alerting show deployment latencies --- litellm/integrations/slack_alerting.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/litellm/integrations/slack_alerting.py b/litellm/integrations/slack_alerting.py index adc56698c39..d4f52f590cf 100644 --- a/litellm/integrations/slack_alerting.py +++ b/litellm/integrations/slack_alerting.py @@ -167,6 +167,14 @@ class SlackAlerting: _deployment_latencies = metadata["_latency_per_deployment"] if len(_deployment_latencies) == 0: return None + try: + # try sorting deployments by latency + _deployment_latencies = sorted( + _deployment_latencies.items(), key=lambda x: x[1] + ) + _deployment_latencies = dict(_deployment_latencies) + except: + pass for api_base, latency in _deployment_latencies.items(): _message_to_send += f"\n{api_base}: {round(latency,2)}s" _message_to_send = "```" + _message_to_send + "```" From 87aad0d2c8346ae7c4efd402fdbf92313e97f126 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 15:59:10 -0700 Subject: [PATCH 50/62] fix(router.py): fix router should retry logic --- litellm/router.py | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index b23efa71d31..a7c75945dd3 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1618,32 +1618,24 @@ class Router: def _router_should_retry( self, e: Exception, remaining_retries: int, num_retries: int ): - if "No models available" in str(e): + """ + Calculate back-off, then retry + """ + if hasattr(e, "response") and hasattr(e.response, "headers"): + timeout = litellm._calculate_retry_after( + remaining_retries=remaining_retries, + max_retries=num_retries, + response_headers=e.response.headers, + min_timeout=self.retry_after, + ) + time.sleep(timeout) + else: timeout = litellm._calculate_retry_after( remaining_retries=remaining_retries, max_retries=num_retries, min_timeout=self.retry_after, ) time.sleep(timeout) - elif hasattr(e, "status_code") and litellm._should_retry( - status_code=e.status_code - ): - if hasattr(e, "response") and hasattr(e.response, "headers"): - timeout = litellm._calculate_retry_after( - remaining_retries=remaining_retries, - max_retries=num_retries, - response_headers=e.response.headers, - min_timeout=self.retry_after, - ) - else: - timeout = litellm._calculate_retry_after( - remaining_retries=remaining_retries, - max_retries=num_retries, - min_timeout=self.retry_after, - ) - time.sleep(timeout) - else: - raise e def function_with_retries(self, *args, **kwargs): """ @@ -1664,9 +1656,6 @@ class Router: return response except Exception as e: original_exception = e - verbose_router_logger.debug( - f"num retries in function with retries: {num_retries}" - ) ### CHECK IF RATE LIMIT / CONTEXT WINDOW ERROR if ( isinstance(original_exception, litellm.ContextWindowExceededError) From 0e891528b6d336e2b58e670a9aa2aa9b3f79eb6f Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 16:01:50 -0700 Subject: [PATCH 51/62] ui - fix bug showing mdoels to pic --- .../src/components/create_key_button.tsx | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/ui/litellm-dashboard/src/components/create_key_button.tsx b/ui/litellm-dashboard/src/components/create_key_button.tsx index 976ac2ccfdc..648eee9cadf 100644 --- a/ui/litellm-dashboard/src/components/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/create_key_button.tsx @@ -39,6 +39,7 @@ const CreateKey: React.FC = ({ const [apiKey, setApiKey] = useState(null); const [softBudget, setSoftBudget] = useState(null); const [userModels, setUserModels] = useState([]); + const [modelsToPick, setModelsToPick] = useState([]); const handleOk = () => { setIsModalVisible(false); form.resetFields(); @@ -94,6 +95,30 @@ const CreateKey: React.FC = ({ const handleCopy = () => { message.success('API Key copied to clipboard'); }; + + useEffect(() => { + let tempModelsToPick = []; + + if (team) { + if (team.models.length > 0) { + if (team.models.includes("all-proxy-models")) { + // if the team has all-proxy-models show all available models + tempModelsToPick = userModels; + } else { + // show team models + tempModelsToPick = team.models; + } + } else { + // show all available models if the team has no models set + tempModelsToPick = userModels; + } + } else { + // no team set, show all available models + tempModelsToPick = userModels; + } + + setModelsToPick(tempModelsToPick); + }, [team, userModels]); return ( @@ -161,30 +186,15 @@ const CreateKey: React.FC = ({ - {team && team.models ? ( - team.models.includes("all-proxy-models") ? ( - userModels.map((model: string) => ( + { + modelsToPick.map((model: string) => ( ( ) )) - ) : ( - team.models.map((model: string) => ( - - )) - ) - ) : ( - userModels.map((model: string) => ( - - )) - )} - + } From d9e0d7ce52f30c3acd9cae6a3504058bdf296bd9 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 16:37:09 -0700 Subject: [PATCH 52/62] test: replace flaky endpoint --- litellm/proxy/_super_secret_config.yaml | 15 +++++++++++++++ litellm/tests/test_least_busy_routing.py | 15 ++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/litellm/proxy/_super_secret_config.yaml b/litellm/proxy/_super_secret_config.yaml index 2e8183cb2d5..0e1b4b2e136 100644 --- a/litellm/proxy/_super_secret_config.yaml +++ b/litellm/proxy/_super_secret_config.yaml @@ -4,5 +4,20 @@ model_list: api_key: my-fake-key model: openai/my-fake-model model_name: fake-openai-endpoint +- litellm_params: + api_base: http://0.0.0.0:8080 + api_key: my-fake-key + model: openai/my-fake-model-2 + model_name: fake-openai-endpoint +- litellm_params: + api_base: http://0.0.0.0:8080 + api_key: my-fake-key + model: openai/my-fake-model-3 + model_name: fake-openai-endpoint +- litellm_params: + api_base: http://0.0.0.0:8080 + api_key: my-fake-key + model: openai/my-fake-model-4 + model_name: fake-openai-endpoint router_settings: num_retries: 0 \ No newline at end of file diff --git a/litellm/tests/test_least_busy_routing.py b/litellm/tests/test_least_busy_routing.py index 782d5b343a6..cb9d59e7557 100644 --- a/litellm/tests/test_least_busy_routing.py +++ b/litellm/tests/test_least_busy_routing.py @@ -201,6 +201,7 @@ async def test_router_atext_completion_streaming(): @pytest.mark.asyncio async def test_router_completion_streaming(): + litellm.set_verbose = True messages = [ {"role": "user", "content": "Hello, can you generate a 500 words poem?"} ] @@ -219,9 +220,9 @@ async def test_router_completion_streaming(): { "model_name": "azure-model", "litellm_params": { - "model": "azure/gpt-35-turbo", - "api_key": "os.environ/AZURE_EUROPE_API_KEY", - "api_base": "https://my-endpoint-europe-berri-992.openai.azure.com", + "model": "azure/gpt-turbo", + "api_key": "os.environ/AZURE_FRANCE_API_KEY", + "api_base": "https://openai-france-1234.openai.azure.com", "rpm": 6, }, "model_info": {"id": 2}, @@ -229,9 +230,9 @@ async def test_router_completion_streaming(): { "model_name": "azure-model", "litellm_params": { - "model": "azure/gpt-35-turbo", - "api_key": "os.environ/AZURE_CANADA_API_KEY", - "api_base": "https://my-endpoint-canada-berri992.openai.azure.com", + "model": "azure/gpt-turbo", + "api_key": "os.environ/AZURE_FRANCE_API_KEY", + "api_base": "https://openai-france-1234.openai.azure.com", "rpm": 6, }, "model_info": {"id": 3}, @@ -262,4 +263,4 @@ async def test_router_completion_streaming(): ## check if calls equally distributed cache_dict = router.cache.get_cache(key=cache_key) for k, v in cache_dict.items(): - assert v == 1 + assert v == 1, f"Failed. K={k} called v={v} times, cache_dict={cache_dict}" From ec19c1654bc2ae83bb2c078a8fd0545b3e8ee30c Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 17:22:50 -0700 Subject: [PATCH 53/62] fix(router.py): set initial value of default litellm params to none --- litellm/router.py | 7 ++++++- litellm/tests/test_router_timeout.py | 11 ++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/litellm/router.py b/litellm/router.py index a7c75945dd3..cd7c72c4a58 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -72,7 +72,9 @@ class Router: ## RELIABILITY ## num_retries: Optional[int] = None, timeout: Optional[float] = None, - default_litellm_params={}, # default params for Router.chat.completion.create + default_litellm_params: Optional[ + dict + ] = None, # default params for Router.chat.completion.create default_max_parallel_requests: Optional[int] = None, set_verbose: bool = False, debug_level: Literal["DEBUG", "INFO"] = "INFO", @@ -158,6 +160,7 @@ class Router: router = Router(model_list=model_list, fallbacks=[{"azure-gpt-3.5-turbo": "openai-gpt-3.5-turbo"}]) ``` """ + if semaphore: self.semaphore = semaphore self.set_verbose = set_verbose @@ -260,6 +263,7 @@ class Router: ) # dict to store aliases for router, ex. {"gpt-4": "gpt-3.5-turbo"}, all requests with gpt-4 -> get routed to gpt-3.5-turbo group # make Router.chat.completions.create compatible for openai.chat.completions.create + default_litellm_params = default_litellm_params or {} self.chat = litellm.Chat(params=default_litellm_params, router_obj=self) # default litellm args @@ -475,6 +479,7 @@ class Router: ) kwargs["model_info"] = deployment.get("model_info", {}) data = deployment["litellm_params"].copy() + model_name = data["model"] for k, v in self.default_litellm_params.items(): if ( diff --git a/litellm/tests/test_router_timeout.py b/litellm/tests/test_router_timeout.py index dff30113be2..4f99d1a99ec 100644 --- a/litellm/tests/test_router_timeout.py +++ b/litellm/tests/test_router_timeout.py @@ -89,15 +89,15 @@ def test_router_timeouts(): @pytest.mark.asyncio async def test_router_timeouts_bedrock(): - import openai + import openai, uuid # Model list for OpenAI and Anthropic models - model_list = [ + _model_list = [ { "model_name": "bedrock", "litellm_params": { "model": "bedrock/anthropic.claude-instant-v1", - "timeout": 0.001, + "timeout": 0.00001, }, "tpm": 80000, }, @@ -105,17 +105,18 @@ async def test_router_timeouts_bedrock(): # Configure router router = Router( - model_list=model_list, + model_list=_model_list, routing_strategy="usage-based-routing", debug_level="DEBUG", set_verbose=True, + num_retries=0, ) litellm.set_verbose = True try: response = await router.acompletion( model="bedrock", - messages=[{"role": "user", "content": "hello, who are u"}], + messages=[{"role": "user", "content": f"hello, who are u {uuid.uuid4()}"}], ) print(response) pytest.fail("Did not raise error `openai.APITimeoutError`") From de8f928bdd763572717da08d1fcc18771da59f50 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 17:28:30 -0700 Subject: [PATCH 54/62] ui - new build --- litellm/proxy/_experimental/out/404.html | 2 +- .../out/_next/static/chunks/app/page-508c39694bd40fe9.js | 1 + .../out/_next/static/chunks/app/page-781ca5f151d78d1d.js | 1 - .../_buildManifest.js | 0 .../_ssgManifest.js | 0 litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../out/_next/static/chunks/app/page-508c39694bd40fe9.js | 1 + .../out/_next/static/chunks/app/page-781ca5f151d78d1d.js | 1 - .../_buildManifest.js | 0 .../_ssgManifest.js | 0 ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- 14 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-508c39694bd40fe9.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-781ca5f151d78d1d.js rename litellm/proxy/_experimental/out/_next/static/{PtTtxXIYvdjQsvRgdITlk => kbGdRQFfI6W3bEwfzmJDI}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{PtTtxXIYvdjQsvRgdITlk => kbGdRQFfI6W3bEwfzmJDI}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-508c39694bd40fe9.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-781ca5f151d78d1d.js rename ui/litellm-dashboard/out/_next/static/{PtTtxXIYvdjQsvRgdITlk => kbGdRQFfI6W3bEwfzmJDI}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{PtTtxXIYvdjQsvRgdITlk => kbGdRQFfI6W3bEwfzmJDI}/_ssgManifest.js (100%) diff --git a/litellm/proxy/_experimental/out/404.html b/litellm/proxy/_experimental/out/404.html index 310a90e222d..6d3d33c80bf 100644 --- a/litellm/proxy/_experimental/out/404.html +++ b/litellm/proxy/_experimental/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/litellm/proxy/_experimental/out/_next/static/chunks/app/page-508c39694bd40fe9.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-508c39694bd40fe9.js new file mode 100644 index 00000000000..290d55662cb --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-508c39694bd40fe9.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,t){Promise.resolve().then(t.bind(t,27125))},27125:function(e,l,t){"use strict";t.r(l),t.d(l,{default:function(){return lf}});var s,a,r=t(3827),n=t(64090),o=t(47907),i=t(8792),c=t(40491),d=t(65270),m=e=>{let{userID:l,userRole:t,userEmail:s,showSSOBanner:a}=e;console.log("User ID:",l),console.log("userEmail:",s),console.log("showSSOBanner:",a);let n=[{key:"1",label:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["Role: ",t]}),(0,r.jsxs)("p",{children:["ID: ",l]})]})}];return(0,r.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,r.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,r.jsx)("div",{className:"flex flex-col items-center",children:(0,r.jsx)(i.default,{href:"/",children:(0,r.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,r.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,r.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,r.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,r.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Request hosted proxy"})}):null,(0,r.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,r.jsx)(c.Z,{menu:{items:n},children:(0,r.jsx)(d.Z,{children:s})})})]})]})},h=t(80588);let u=async()=>{try{let e=await fetch("https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"),l=await e.json();return console.log("received data: ".concat(l)),l}catch(e){throw console.error("Failed to get model cost map:",e),e}},x=async(e,l)=>{try{let t=await fetch("/model/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model created successfully. Wait 60s and refresh on 'All Models' page"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,l)=>{console.log("model_id in model delete call: ".concat(l));try{let t=await fetch("/model/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model deleted successfully. Restart server to see this."),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,l)=>{try{console.log("in keyDeleteCall:",l);let t=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,l)=>{try{console.log("in teamDeleteCall:",l);let t=await fetch("/team/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete team: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to delete key:",e),e}},Z=async function(e,l,t){let s=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,r=arguments.length>5?arguments[5]:void 0;try{let n="/user/info";"App Owner"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),"App User"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),console.log("in userInfoCall viewAll=",s),s&&r&&null!=a&&void 0!=a&&(n="".concat(n,"?view_all=true&page=").concat(a,"&page_size=").concat(r));let o=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let i=await o.json();return console.log("API Response:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},w=async(e,l)=>{try{let t="/team/info";l&&(t="".concat(t,"?team_id=").concat(l)),console.log("in teamInfoCall");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let l=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,l,t)=>{try{let l=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log("modelInfoCall:",t),t}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,l,t,s)=>{try{let l="/model/metrics";s&&(l="".concat(l,"?_selected_model_group=").concat(s));let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,l,t)=>{try{let l=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,l)=>{try{let t="/global/spend/logs";console.log("in keySpendLogsCall:",t);let s=await fetch("".concat(t,"?api_key=").concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let l="/global/spend/teams";console.log("in teamSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let l="/global/spend/tags";console.log("in tagsSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},C=async(e,l,t,s,a,r)=>{try{console.log("user role in spend logs call: ".concat(t));let l="/spend/logs";l="App Owner"==t?"".concat(l,"?user_id=").concat(s,"&start_date=").concat(a,"&end_date=").concat(r):"".concat(l,"?start_date=").concat(a,"&end_date=").concat(r);let n=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},I=async e=>{try{let l=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{let l=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,l)=>{try{l&&JSON.stringify({api_key:l});let t={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};l&&(t.body=JSON.stringify({api_key:l}));let s=await fetch("/global/spend/end_users",t);if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},E=async e=>{try{let l=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},F=async(e,l)=>{try{let t=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},O=async e=>{try{let l="/user/get_requests";console.log("in userGetRequesedtModelsCall:",l);let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to get requested models:",e),e}},M=async(e,l)=>{try{let t="/user/get_users?role=".concat(l);console.log("in userGetAllUsersCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},R=async(e,l)=>{try{console.log("Form Values in teamCreateCall:",l);let t=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,l)=>{try{console.log("Form Values in keyUpdateCall:",l);let t=await fetch("/key/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update key Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,l)=>{try{console.log("Form Values in teamUpateCall:",l);let t=await fetch("/team/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update team: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update Team Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async(e,l)=>{try{console.log("Form Values in modelUpateCall:",l);let t=await fetch("/model/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update model: "+e,20),console.error("Error update from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update model Response:",s),s}catch(e){throw console.error("Failed to update model:",e),e}},z=async(e,l,t)=>{try{console.log("Form Values in teamMemberAddCall:",t);let s=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:l,member:t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,l,t)=>{try{console.log("Form Values in userUpdateUserCall:",l);let s={...l};null!==t&&(s.user_role=t),s=JSON.stringify(s);let a=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:s});if(!a.ok){let e=await a.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await a.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,l)=>{try{let t=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,l)=>{try{let t="/health/services?service=".concat(l);console.log("Checking Slack Budget Alerts service health");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed ".concat(l," service health check ")+e),Error(e)}let a=await s.json();return h.ZP.success("Test request to ".concat(l," made - check logs/alerts on ").concat(l," to verify")),a}catch(e){throw console.error("Failed to perform health check:",e),e}},V=async(e,l,t)=>{try{let l=await fetch("/get/config/callbacks",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},G=async(e,l)=>{try{let t=await fetch("/config/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},W=async e=>{try{let l=await fetch("/health",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to call /health:",e),e}};var J=t(10384),Y=t(46453),H=t(2179),$=t(52273),X=t(26780),Q=t(15595),ee=t(6698),el=t(71801),et=t(42440),es=t(42308),ea=t(50670),er=t(81583),en=t(99129),eo=t(44839),ei=t(88707),ec=t(1861);let{Option:ed}=ea.default;var em=e=>{let{userID:l,team:t,userRole:s,accessToken:a,data:o,setData:i}=e,[c]=er.Z.useForm(),[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(null),[p,g]=(0,n.useState)(null),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),b=()=>{m(!1),c.resetFields()},_=()=>{m(!1),x(null),c.resetFields()};(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==a){let e=(await v(a,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),y(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,s]);let k=async e=>{try{h.ZP.info("Making API Call"),m(!0);let t=await j(a,l,e);console.log("key create Response:",t),i(e=>e?[...e,t]:[t]),x(t.key),g(t.soft_budget),h.ZP.success("API Key Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e)}};return(0,n.useEffect)(()=>{w(t&&t.models.length>0?t.models.includes("all-proxy-models")?f:t.models:f)},[t,f]),(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,r.jsx)(en.Z,{title:"Create Key",visible:d,width:800,footer:null,onOk:b,onCancel:_,children:(0,r.jsxs)(er.Z,{form:c,onFinish:k,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:t?t.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t?t.team_alias:"",disabled:!0})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&c.setFieldsValue({models:["all-team-models"]})},children:[(0,r.jsx)(ed,{value:"all-team-models",children:"All Team Models"},"all-team-models"),Z.map(e=>(0,r.jsx)(ed,{value:e,children:e},e))]})}),(0,r.jsxs)(X.Z,{className:"mt-20 mb-8",children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)("b",{children:"Optional Settings"})}),(0,r.jsxs)(Q.Z,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==t?void 0:t.max_budget)!==null&&(null==t?void 0:t.max_budget)!==void 0?null==t?void 0:t.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.max_budget&&l>t.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(t.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==t?void 0:t.budget_duration)!==null&&(null==t?void 0:t.budget_duration)!==void 0?null==t?void 0:t.budget_duration:"None"),children:(0,r.jsxs)(ea.default,{defaultValue:null,placeholder:"n/a",children:[(0,r.jsx)(ea.default.Option,{value:"24h",children:"daily"}),(0,r.jsx)(ea.default.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==t?void 0:t.tpm_limit)!==null&&(null==t?void 0:t.tpm_limit)!==void 0?null==t?void 0:t.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.tpm_limit&&l>t.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(t.tpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==t?void 0:t.rpm_limit)!==null&&(null==t?void 0:t.rpm_limit)!==void 0?null==t?void 0:t.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.rpm_limit&&l>t.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(t.rpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Key"})})]})}),u&&(0,r.jsx)(en.Z,{visible:d,onOk:b,onCancel:_,footer:null,children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Save your Key"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,r.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,r.jsx)(J.Z,{numColSpan:1,children:null!=u?(0,r.jsxs)("div",{children:[(0,r.jsx)(el.Z,{className:"mt-3",children:"API Key:"}),(0,r.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,r.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:u})}),(0,r.jsx)(es.CopyToClipboard,{text:u,onCopy:()=>{h.ZP.success("API Key copied to clipboard")},children:(0,r.jsx)(H.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,r.jsx)(el.Z,{children:"Key being created, this might take 30s"})})]})})]})},eh=t(9454),eu=t(98941),ex=t(33393),ep=t(5),ej=t(9853),eg=t(13810),ef=t(39290),ey=t(66952),eZ=t(61244),ew=t(10827),eb=t(3851),e_=t(2044),ek=t(64167),ev=t(74480),eS=t(7178),eA=t(95093),eN=t(27166);let{Option:eC}=ea.default;var eI=e=>{let{userID:l,userRole:t,accessToken:s,selectedTeam:a,data:o,setData:i,teams:c}=e,[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(!1),[p,j]=(0,n.useState)(null),[g,y]=n.useState(null),[Z,w]=(0,n.useState)(null),[b,_]=(0,n.useState)(null),[k,A]=(0,n.useState)(""),[N,C]=(0,n.useState)(!1),[I,P]=(0,n.useState)(null),[T,E]=(0,n.useState)([]),F=new Set,[O,M]=(0,n.useState)(F);(0,n.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==s&&null!==t){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),E(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[s,l,t]),(0,n.useEffect)(()=>{if(c){let e=new Set;c.forEach((l,t)=>{let s=l.team_id;e.add(s)}),M(e)}},[c]);let R=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),P(e),C(!0)},U=async e=>{if(null==s)return;let l=e.token;e.key=l,console.log("handleEditSubmit:",e);let t=await L(s,e);console.log("handleEditSubmit: newKeyValues",t),o&&i(o.map(e=>e.token===l?t:e)),h.ZP.success("Key updated successfully"),C(!1),P(null)},D=async e=>{try{if(null==s||null==e)return;console.log("accessToken: ".concat(s,"; token: ").concat(e.token));let l=await S(s,e.token);console.log("Response:",l),_(l);try{let e=await q(s,l);console.log("Response2:",e);let t=[...l,...e.response];_(t),A(e.predicted_spend),console.log("Combined Data:",t)}catch(e){console.error("There was an error fetching the predicted data",e)}}catch(e){console.error("There was an error fetching the data",e)}};(0,n.useEffect)(()=>{D(Z)},[Z]);let z=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=o&&(j(e.token),localStorage.removeItem("userData"+l),x(!0))},B=async()=>{if(null!=p&&null!=o){try{await f(s,p);let e=o.filter(e=>e.token!==p);i(e)}catch(e){console.error("Error deleting the key:",e)}x(!1),j(null)}};if(null!=o)return console.log("RERENDER TRIGGERED"),(0,r.jsxs)("div",{children:[(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,r.jsxs)(ew.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Key Alias"}),(0,r.jsx)(ev.Z,{children:"Secret Key"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"})]})}),(0,r.jsx)(eb.Z,{children:o.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(a){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(O.has(e.team_id),", selectedTeam id: ").concat(a.team_id)),(null!=a.team_id||null===e.team_id||O.has(e.team_id))&&e.team_id!=a.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,r.jsx)(el.Z,{children:e.key_alias}):(0,r.jsx)(el.Z,{children:"Not Set"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.key_name})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,r.jsx)(e_.Z,{children:null!=e.max_budget?(0,r.jsx)(el.Z,{children:e.max_budget}):(0,r.jsx)(el.Z,{children:"Unlimited"})}),(0,r.jsx)(e_.Z,{children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(r.Fragment,{children:a&&a.models&&a.models.length>0?a.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(el.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{onClick:()=>{w(e),y(e.id)},icon:eh.Z,size:"sm"}),(0,r.jsx)(ef.Z,{open:null!==g,onClose:()=>{y(null),w(null)},children:(0,r.jsx)(ey.Z,{children:Z&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",children:[(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(Z.spend).toFixed(4)}catch(e){return Z.spend}})()})})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.max_budget?(0,r.jsx)(r.Fragment,{children:Z.max_budget}):(0,r.jsx)(r.Fragment,{children:"Unlimited"})})})]},e.name),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.expires?(0,r.jsx)(r.Fragment,{children:new Date(Z.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,r.jsx)(r.Fragment,{children:"Never"})})})]},e.name)]}),(0,r.jsx)(eg.Z,{className:"mt-6 mb-6",children:b&&(0,r.jsx)(ej.Z,{className:"mt-6",data:b,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})}),(0,r.jsx)(et.Z,{children:"Metadata"}),(0,r.jsx)(el.Z,{children:JSON.stringify(Z.metadata)}),(0,r.jsx)(H.Z,{variant:"light",className:"mx-auto flex items-center",onClick:()=>{y(null),w(null)},children:"Close"})]})})}),(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>R(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>z(e),icon:ex.Z,size:"sm"})]})]},e.token)})})]}),u&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:B,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{x(!1),j(null)},children:"Cancel"})]})]})]})})]}),I&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,token:s,onSubmit:o}=e,[i]=er.Z.useForm(),[d,m]=(0,n.useState)(a),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)(!1);return(0,r.jsx)(en.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{i.validateFields().then(e=>{i.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:i,onFinish:U,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let t=l.filter(e=>!d.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!d.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(t)),t.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(t,"Team models: ").concat(d.models)):Promise.resolve()}}],children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(eC,{value:"all-team-models",children:"All Team Models"},"all-team-models"),d&&d.models?d.models.includes("all-proxy-models")?T.filter(e=>"all-proxy-models"!==e).map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):d.models.map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):T.map(e=>(0,r.jsx)(eC,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==d?void 0:d.max_budget)!==null&&(null==d?void 0:d.max_budget)!==void 0?null==d?void 0:d.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&d&&null!==d.max_budget&&l>d.max_budget)throw console.log("keyTeam.max_budget: ".concat(d.max_budget)),Error("Budget cannot exceed team max budget: $".concat(d.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"token",name:"token",hidden:!0}),(0,r.jsx)(er.Z.Item,{label:"Team",name:"team_id",help:"the team this key belongs to",children:(0,r.jsx)(eA.Z,{value:s.team_alias,children:null==c?void 0:c.map((e,l)=>(0,r.jsx)(eN.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:N,onCancel:()=>{C(!1),P(null)},token:I,onSubmit:U})]})},eP=t(76032),eT=t(35152),eE=e=>{let{userID:l,userRole:t,accessToken:s,userSpend:a,selectedTeam:o}=e;console.log("userSpend: ".concat(a));let[i,c]=(0,n.useState)(null!==a?a:0),[d,m]=(0,n.useState)(0),[h,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{let e=async()=>{if(s&&l&&t&&"Admin"===t&&null==a)try{let e=await b(s);e&&(e.spend?c(e.spend):c(0),e.max_budget?m(e.max_budget):m(0))}catch(e){console.error("Error fetching global spend data:",e)}};(async()=>{try{if(null===l||null===t)return;if(null!==s){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),u(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,s,l]),(0,n.useEffect)(()=>{null!==a&&c(a)},[a]);let x=[];o&&o.models&&(x=o.models),x&&x.includes("all-proxy-models")?(console.log("user models:",h),x=h):x&&x.includes("all-team-models")?x=o.models:x&&0===x.length&&(x=h);let p=void 0!==i?i.toFixed(4):null;return console.log("spend in view user spend: ".concat(i)),(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:["Total Spend"," "]}),(0,r.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",p]})]}),(0,r.jsx)("div",{className:"ml-auto",children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Team Models"})}),(0,r.jsx)(Q.Z,{className:"absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs",children:(0,r.jsx)(eP.Z,{children:x.map(e=>(0,r.jsx)(eT.Z,{children:(0,r.jsx)(el.Z,{children:e})},e))})})]})})]})},eF=e=>{let{userID:l,userRole:t,selectedTeam:s,accessToken:a}=e,[o,i]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==a){let e=(await v(a,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),i(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,t]);let c=[];return s&&s.models&&(c=s.models),c&&c.includes("all-proxy-models")&&(console.log("user models:",o),c=o),(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"mb-5",children:(0,r.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==s?void 0:s.team_alias})})})},eO=e=>{let l,{teams:t,setSelectedTeam:s,userRole:a}=e,o={models:[],team_id:null,team_alias:"Default Team"},[i,c]=(0,n.useState)(o);return(l="App User"===a?t:t?[...t,o]:[o],"App User"===a)?null:(0,r.jsxs)("div",{className:"mt-5 mb-5",children:[(0,r.jsx)(et.Z,{children:"Select Team"}),(0,r.jsx)(el.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,r.jsxs)(el.Z,{className:"mt-3 mb-3",children:[(0,r.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,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>s(e),children:e.team_alias},l))}):(0,r.jsxs)(el.Z,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]})},eM=t(37963),eR=t(36083);console.log("isLocal:",!1);var eL=e=>{let{userID:l,userRole:t,teams:s,keys:a,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:m,setKeys:h}=e,[u,x]=(0,n.useState)(null),p=(0,o.useSearchParams)();p.get("viewSpend"),(0,o.useRouter)();let j=p.get("token"),[g,f]=(0,n.useState)(null),[y,w]=(0,n.useState)(null),[_,k]=(0,n.useState)([]),S={models:[],team_alias:"Default Team",team_id:null},[A,N]=(0,n.useState)(s?s[0]:S);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(j){let e=(0,eM.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),i(l)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&g&&t&&!a&&!u){let e=sessionStorage.getItem("userModels"+l);e?k(JSON.parse(e)):(async()=>{try{let e=await Z(g,l,t,!1,null,null);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==t){let e=await b(g);x(e),console.log("globalSpend:",e)}else x(e.user_info);h(e.keys),m(e.teams);let s=[...e.teams];s.length>0?(console.log("response['teams']: ".concat(s)),N(s[0])):N(S),sessionStorage.setItem("userData"+l,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(e.user_info));let a=(await v(g,l,t)).data.map(e=>e.id);console.log("available_model_names:",a),k(a),console.log("userModels:",_),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,j,g,a,t]),(0,n.useEffect)(()=>{if(null!==a&&null!=A){let e=0;for(let l of a)A.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===A.team_id&&(e+=l.spend);w(e)}else if(null!==a){let e=0;for(let l of a)e+=l.spend;w(e)}},[A]),null==l||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==g)return null;if(null==t&&i("App Owner"),t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",A),console.log("teamSpend: ".concat(y)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eF,{userID:l,userRole:t,selectedTeam:A||null,accessToken:g}),(0,r.jsx)(eE,{userID:l,userRole:t,accessToken:g,userSpend:y,selectedTeam:A||null}),(0,r.jsx)(eI,{userID:l,userRole:t,accessToken:g,selectedTeam:A||null,data:a,setData:h,teams:s}),(0,r.jsx)(em,{userID:l,team:A||null,userRole:t,accessToken:g,data:a,setData:h},A?A.team_id:null),(0,r.jsx)(eO,{teams:s,setSelectedTeam:N,userRole:t})]})})})},eU=t(92836),eD=t(26734),ez=t(41608),eB=t(32126),eq=t(23682),eK=t(47047),eV=t(76628),eG=t(38302),eW=t(28683),eJ=t(1460),eY=t(78578),eH=t(63954),e$=e=>{let{modelID:l,accessToken:t}=e,[s,a]=(0,n.useState)(!1),o=async()=>{try{h.ZP.info("Making API Call"),a(!0);let e=await p(t,l);console.log("model delete Response:",e),h.ZP.success("Model ".concat(l," deleted successfully")),a(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(eZ.Z,{onClick:()=>a(!0),icon:ex.Z,size:"sm"}),(0,r.jsx)(en.Z,{open:s,onOk:o,okType:"danger",onCancel:()=>a(!1),children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Delete Model"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Model ID: ",(0,r.jsx)("b",{children:l})]})})]})})]})},eX=t(97766),eQ=t(46495);let{Title:e0,Link:e1}=eR.default;(s=a||(a={})).OpenAI="OpenAI",s.Azure="Azure",s.Anthropic="Anthropic",s.Google_AI_Studio="Gemini (Google AI Studio)",s.Bedrock="Amazon Bedrock",s.OpenAI_Compatible="OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)",s.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)";let e2={OpenAI:"openai",Azure:"azure",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai"},e4=async(e,l,t)=>{try{let s=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(s)),console.log("received type of deployments: ".concat(typeof s)),s.forEach(async t=>{console.log("litellm_model: ".concat(t));let s={},a={};s.model=t;let r="";for(let[l,t]of Object.entries(e))if(""!==t){if("model_name"==l)r+=t;else if("custom_llm_provider"==l)continue;else if("model"==l)continue;else if("base_model"===l)a[l]=t;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",t);let e={};if(t&&void 0!=t){try{e=JSON.parse(t)}catch(e){throw h.ZP.error("Failed to parse LiteLLM Extra Params: "+e,20),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,t]of Object.entries(e))s[l]=t}}else s[l]=t}let n={model_name:r,litellm_params:s,model_info:a},o=await x(l,n);console.log("response for model create call: ".concat(o.data))}),t.resetFields()}catch(e){h.ZP.error("Failed to create model: "+e,20)}};var e8=e=>{var l,t,s;let{accessToken:o,token:i,userRole:c,userID:d,modelData:m={data:[]},setModelData:x}=e,[p,j]=(0,n.useState)([]),[g]=er.Z.useForm(),[f,y]=(0,n.useState)(null),[Z,w]=(0,n.useState)(""),[b,k]=(0,n.useState)([]),v=Object.values(a).filter(e=>isNaN(Number(e))),[S,A]=(0,n.useState)("OpenAI"),[N,C]=(0,n.useState)(""),[I,P]=(0,n.useState)(!1),[T,E]=(0,n.useState)(null),[F,M]=(0,n.useState)([]),[R,L]=(0,n.useState)(null),U=e=>{E(e),P(!0)},z=async e=>{if(console.log("handleEditSubmit:",e),null==o)return;let l={},t=null;for(let[s,a]of Object.entries(e))"model_id"!==s?l[s]=a:t=a;let s={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",s),await D(o,s),h.ZP.success("Model updated successfully, restart server to see updates"),P(!1),E(null)},B=()=>{w(new Date().toLocaleString())};if((0,n.useEffect)(()=>{if(!o||!i||!c||!d)return;let e=async()=>{try{let e=await _(o,d,c);console.log("Model data response:",e.data),x(e);let l=new Set;for(let t=0;t{let e=await u();console.log("received model cost map data: ".concat(Object.keys(e))),y(e)};null==f&&l(),B()},[o,i,c,d,f,Z]),!m||!o||!i||!c||!d)return(0,r.jsx)("div",{children:"Loading..."});let q=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(f)),null!=f&&"object"==typeof f&&e in f)?f[e].litellm_provider:"openai";if(a){let e=a.split("/"),l=e[0];n=1===e.length?h(a):l}else n="openai";r&&(o=null==r?void 0:r.input_cost_per_token,i=null==r?void 0:r.output_cost_per_token,c=null==r?void 0:r.max_tokens),(null==s?void 0:s.litellm_params)&&(d=Object.fromEntries(Object.entries(null==s?void 0:s.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),m.data[e].provider=n,m.data[e].input_cost=o,m.data[e].output_cost=i,m.data[e].max_tokens=c,m.data[e].api_base=null==s?void 0:null===(t=s.litellm_params)||void 0===t?void 0:t.api_base,m.data[e].cleanedLitellmParams=d,q.push(s.model_name),console.log(m.data[e])}if(c&&"Admin Viewer"==c){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let K=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(a).find(l=>a[l]===e);if(l){let e=e2[l];console.log("mappingResult: ".concat(e));let t=[];"object"==typeof f&&Object.entries(f).forEach(l=>{let[s,a]=l;null!==a&&"object"==typeof a&&"litellm_provider"in a&&(a.litellm_provider===e||a.litellm_provider.includes(e))&&t.push(s)}),k(t),console.log("providerModels: ".concat(b))}},V=async()=>{try{h.ZP.info("Running health check..."),C("");let e=await W(o);C(e)}catch(e){console.error("Error running health check:",e),C("Error running health check")}};return console.log("selectedProvider: ".concat(S)),console.log("providerModels.length: ".concat(b.length)),(0,r.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)(eU.Z,{children:"All Models"}),(0,r.jsx)(eU.Z,{children:"Add Model"}),(0,r.jsx)(eU.Z,{children:(0,r.jsx)("pre",{children:"/health Models"})})]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[Z&&(0,r.jsxs)(el.Z,{children:["Last Refreshed: ",Z]}),(0,r.jsx)(eZ.Z,{icon:eH.Z,variant:"shadow",size:"xs",className:"self-center",onClick:B})]})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(Y.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(el.Z,{children:"Filter by Public Model Name"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:"all",onValueChange:e=>L("all"===e?"all":e),children:[(0,r.jsx)(eN.Z,{value:"all",children:"All Models"}),F.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>L(e),children:e},l))]})]}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Public Model Name "}),(0,r.jsx)(ev.Z,{children:"Provider"}),"Admin"===c&&(0,r.jsx)(ev.Z,{children:"API Base"}),(0,r.jsx)(ev.Z,{children:"Extra litellm Params"}),(0,r.jsx)(ev.Z,{children:"Input Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Output Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Max Tokens"})]})}),(0,r.jsx)(eb.Z,{children:m.data.filter(e=>"all"===R||e.model_name===R||null==R||""===R).map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.model_name})}),(0,r.jsx)(e_.Z,{children:e.provider}),"Admin"===c&&(0,r.jsx)(e_.Z,{children:e.api_base}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Litellm params"})}),(0,r.jsx)(Q.Z,{children:(0,r.jsx)("pre",{children:JSON.stringify(e.cleanedLitellmParams,null,2)})})]})}),(0,r.jsx)(e_.Z,{children:e.input_cost}),(0,r.jsx)(e_.Z,{children:e.output_cost}),(0,r.jsx)(e_.Z,{children:e.max_tokens}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>U(e)}),(0,r.jsx)(e$,{modelID:e.model_info.id,accessToken:o})]})]},l))})]})})]}),(0,r.jsx)(e=>{let{visible:l,onCancel:t,model:s,onSubmit:a}=e,[n]=er.Z.useForm(),o={},i="",c="";if(s){o=s.litellm_params,i=s.model_name;let e=s.model_info;e&&(c=e.id,console.log("model_id: ".concat(c)),o.model_id=c)}return(0,r.jsx)(en.Z,{title:"Edit Model "+i,visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a(e),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:z,initialValues:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,r.jsx)($.Z,{})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"max_retries",name:"max_retries",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:I,onCancel:()=>{P(!1),E(null)},model:T,onSubmit:z})]}),(0,r.jsxs)(eB.Z,{className:"h-full",children:[(0,r.jsx)(e0,{level:2,children:"Add new model"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(er.Z,{form:g,onFinish:()=>{g.validateFields().then(e=>{e4(e,o,g)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.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,r.jsx)(eA.Z,{value:S.toString(),children:v.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>{K(e),A(e)},children:e},l))})}),(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"Vertex AI (Anthropic, Gemini, etc.)"===(s=S.toString())?"gemini-pro":"Anthropic"==s?"claude-3-opus":"Amazon Bedrock"==s?"claude-3-opus":"Gemini (Google AI Studio)"==s?"gemini-pro":"gpt-3.5-turbo"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"LiteLLM Model Name(s)",name:"model",tooltip:"Actual model name used for making litellm.completion() call.",className:"mb-0",children:"Azure"===S?(0,r.jsx)($.Z,{placeholder:"Enter model name"}):b.length>0?(0,r.jsx)(eK.Z,{value:b,children:b.map((e,l)=>(0,r.jsx)(eV.Z,{value:e,children:e},l))}):(0,r.jsx)($.Z,{placeholder:"gpt-3.5-turbo-0125"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Actual model name used for making ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," models with the same 'public name'"]})})]}),"Amazon Bedrock"!=S&&"Vertex AI (Anthropic, Gemini, etc.)"!=S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,r.jsx)($.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==S&&(0,r.jsx)(er.Z.Item,{label:"Organization ID",name:"organization_id",children:(0,r.jsx)($.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,r.jsx)($.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,r.jsx)($.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,r.jsx)(eQ.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;g.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?h.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&h.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,r.jsx)(ec.ZP,{icon:(0,r.jsx)(eX.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==S||"OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)"==S)&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,r.jsx)($.Z,{placeholder:"https://..."})}),"Azure"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Version",name:"api_version",children:(0,r.jsx)($.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==S&&(0,r.jsxs)(er.Z.Item,{label:"Base Model",name:"base_model",children:[(0,r.jsx)($.Z,{placeholder:"azure/gpt-3.5-turbo"}),(0,r.jsxs)(el.Z,{children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from ",(0,r.jsx)(e1,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})]}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"us-east-1"})}),(0,r.jsx)(er.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,r.jsx)(eY.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,r.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Model"})}),(0,r.jsx)(eJ.Z,{title:"Get help on our github",children:(0,r.jsx)(eR.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(el.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,r.jsx)(H.Z,{onClick:V,children:"Run `/health`"}),N&&(0,r.jsx)("pre",{children:JSON.stringify(N,null,2)})]})})]})]})})};let{Option:e5}=ea.default;var e3=e=>{let{userID:l,accessToken:t,teams:s}=e,[a]=er.Z.useForm(),[o,i]=(0,n.useState)(!1),[c,d]=(0,n.useState)(null),[m,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await v(t,l,"any"),s=[];for(let l=0;l{i(!1),a.resetFields()},p=()=>{i(!1),d(null),a.resetFields()},j=async e=>{try{h.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let s=await g(t,null,e);console.log("user create Response:",s),d(s.key),h.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Invite User"}),(0,r.jsxs)(en.Z,{title:"Invite User",visible:o,width:800,footer:null,onOk:x,onCancel:p,children:[(0,r.jsx)(el.Z,{className:"mb-1",children:"Invite a user to login to the Admin UI and create Keys"}),(0,r.jsx)(el.Z,{className:"mb-6",children:(0,r.jsx)("b",{children:"Note: SSO Setup Required for this"})}),(0,r.jsxs)(er.Z,{form:a,onFinish:j,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsx)(er.Z.Item,{label:"User Email",name:"user_email",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",children:(0,r.jsx)(ea.default,{placeholder:"Select Team ID",style:{width:"100%"},children:s?s.map(e=>(0,r.jsx)(e5,{value:e.team_id,children:e.team_alias},e.team_id)):(0,r.jsx)(e5,{value:null,children:"Default Team"},"default")})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create User"})})]})]}),c&&(0,r.jsxs)(en.Z,{title:"User Created Successfully",visible:o,onOk:x,onCancel:p,footer:null,children:[(0,r.jsx)("p",{children:"User has been created to access your proxy. Please Ask them to Log In."}),(0,r.jsx)("br",{}),(0,r.jsx)("p",{children:(0,r.jsx)("b",{children:"Note: This Feature is only supported through SSO on the Admin UI"})})]})]})},e6=e=>{let{accessToken:l,token:t,keys:s,userRole:a,userID:o,teams:i,setKeys:c}=e,[d,m]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(0),[j,g]=n.useState(null),[f,y]=(0,n.useState)(null);if((0,n.useEffect)(()=>{if(!l||!t||!a||!o)return;let e=async()=>{try{let e=await Z(l,null,a,!0,x,25);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};l&&t&&a&&o&&e();let s=async()=>{try{let e=await T(l,null);console.log("user data response:",e),u(e)}catch(e){console.error("There was an error fetching the model data",e)}};a&&("Admin"==a||"Admin Viewer"==a)&&!h&&s()},[l,t,a,o,x]),!d||!l||!t||!a||!o)return(0,r.jsx)("div",{children:"Loading..."});let w=async e=>{try{let t=await T(l,e);console.log("user data response:",t),u(t)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,r.jsx)("div",{style:{width:"100%"},children:(0,r.jsxs)(Y.Z,{className:"gap-2 p-2 h-[80vh] w-full mt-8",children:[(0,r.jsx)(e3,{userID:o,accessToken:l,teams:i}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[80vh] mb-4",children:[(0,r.jsxs)("div",{className:"mb-4 mt-1",children:[(0,r.jsxs)(el.Z,{children:[(0,r.jsx)("b",{children:"Key Owners: "})," Users on LiteLLM that created API Keys. Automatically tracked by LiteLLM"]}),(0,r.jsxs)(el.Z,{className:"mt-1",children:[(0,r.jsx)("b",{children:"End Users: "}),"End Users of your LLM API calls. Tracked When a `user` param is passed in your LLM calls"]})]}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Key Owners"}),(0,r.jsx)(eU.Z,{value:"2",children:"End-Users"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"User ID"}),(0,r.jsx)(ev.Z,{children:"User Email"}),(0,r.jsx)(ev.Z,{children:"User Models"}),(0,r.jsx)(ev.Z,{children:"User Spend ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User Max Budget ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User API Key Aliases"})]})}),(0,r.jsx)(eb.Z,{children:d.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_id}),(0,r.jsx)(e_.Z,{children:e.user_email}),(0,r.jsx)(e_.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,r.jsx)(e_.Z,{children:e.spend?e.spend:0}),(0,r.jsx)(e_.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(Y.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,r.jsx)(ep.Z,{size:"xs",color:"indigo",children:e.key_aliases.filter(e=>null!==e).join(", ")}):(0,r.jsx)(ep.Z,{size:"xs",color:"gray",children:"No Keys"})})})]},e.user_id))})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)("div",{className:"flex-1"}),(0,r.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,r.jsx)(el.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,r.jsx)(eA.Z,{defaultValue:"1",className:"w-3/4",children:null==s?void 0:s.map((e,l)=>{if(e&&null!==e.key_alias&&e.key_alias.length>0)return(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>w(e.token),children:e.key_alias},l)})})]})]}),(0,r.jsxs)(ew.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"End User"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Total Events"})]})}),(0,r.jsx)(eb.Z,{children:null==h?void 0:h.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.end_user}),(0,r.jsx)(e_.Z,{children:e.total_spend}),(0,r.jsx)(e_.Z,{children:e.total_events})]},l))})]})]})]})]})]}),function(){if(!d)return null;let e=Math.ceil(d.length/25);return(0,r.jsxs)("div",{className:"flex justify-between items-center",children:[(0,r.jsxs)("div",{children:["Showing Page ",x+1," of ",e]}),(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:0===x,onClick:()=>p(x-1),children:"← Prev"}),(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",onClick:()=>{p(x+1)},children:"Next →"})]})]})}()]})})},e7=e=>{let{teams:l,searchParams:t,accessToken:s,setTeams:a,userID:o,userRole:i}=e,[c]=er.Z.useForm(),[d]=er.Z.useForm(),{Title:m,Paragraph:u}=eR.default,[x,p]=(0,n.useState)(""),[j,g]=(0,n.useState)(!1),[f,Z]=(0,n.useState)(l?l[0]:null),[b,_]=(0,n.useState)(!1),[k,S]=(0,n.useState)(!1),[A,N]=(0,n.useState)([]),[C,I]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),[E,F]=(0,n.useState)({}),O=e=>{Z(e),g(!0)},M=async e=>{let t=e.team_id;if(console.log("handleEditSubmit:",e),null==s)return;let r=await U(s,e);l&&a(l.map(e=>e.team_id===t?r.data:e)),h.ZP.success("Team updated successfully"),g(!1),Z(null)},L=async e=>{T(e),I(!0)},D=async()=>{if(null!=P&&null!=l&&null!=s){try{await y(s,P);let e=l.filter(e=>e.team_id!==P);a(e)}catch(e){console.error("Error deleting the team:",e)}I(!1),T(null)}};(0,n.useEffect)(()=>{let e=async()=>{try{if(null===o||null===i||null===s||null===l)return;console.log("fetching team info:");let e={};for(let t=0;t<(null==l?void 0:l.length);t++){let a=l[t].team_id,r=await w(s,a);console.log("teamInfo response:",r),null!==r&&(e={...e,[a]:r})}F(e)}catch(e){console.error("Error fetching team info:",e)}};(async()=>{try{if(null===o||null===i)return;if(null!==s){let e=(await v(s,o,i)).data.map(e=>e.id);console.log("available_model_names:",e),N(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,o,i,l]);let B=async e=>{try{if(null!=s){h.ZP.info("Creating Team");let t=await R(s,e);null!==l?a([...l,t]):a([t]),console.log("response for team create call: ".concat(t)),h.ZP.success("Team created"),_(!1)}}catch(e){console.error("Error creating the team:",e),h.ZP.error("Error creating the team: "+e,20)}},q=async e=>{try{if(null!=s&&null!=l){h.ZP.info("Adding Member");let t={role:"user",user_email:e.user_email,user_id:e.user_id},r=await z(s,f.team_id,t);console.log("response for team create call: ".concat(r.data));let n=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(r.data.team_id)),e.team_id===r.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...l];e[n]=r.data,a(e),Z(r.data)}S(!1)}}catch(e){console.error("Error creating the team:",e)}};return console.log("received teams ".concat(l)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"All Teams"}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Team Name"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"}),(0,r.jsx)(ev.Z,{children:"Info"})]})}),(0,r.jsx)(eb.Z,{children:l&&l.length>0?l.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,r.jsx)(e_.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,r.jsxs)(el.Z,{children:["TPM:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].keys&&E[e.team_id].keys.length," Keys"]}),(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].team_info&&E[e.team_id].team_info.members_with_roles&&E[e.team_id].team_info.members_with_roles.length," Members"]})]}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>O(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>L(e.team_id),icon:ex.Z,size:"sm"})]})]},e.team_id)):null})]}),C&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:D,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{I(!1),T(null)},children:"Cancel"})]})]})]})})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>_(!0),children:"+ Create New Team"}),(0,r.jsx)(en.Z,{title:"Create Team",visible:b,width:800,footer:null,onOk:()=>{_(!1),c.resetFields()},onCancel:()=>{_(!1),c.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:B,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"Team Members"}),(0,r.jsx)(u,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>{Z(e)},children:e.team_alias},l))}):(0,r.jsxs)(u,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:f?f.members_with_roles.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.role})]},l)):null})]})}),f&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,team:s,onSubmit:a}=e,[n]=er.Z.useForm();return(0,r.jsx)(en.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a({...e,team_id:s.team_id}),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:M,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A&&A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:j,onCancel:()=>{g(!1),Z(null)},team:f,onSubmit:M})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto mb-5",onClick:()=>S(!0),children:"+ Add member"}),(0,r.jsx)(en.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{S(!1),d.resetFields()},onCancel:()=>{S(!1),d.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},e9=t(18190),le=e=>{let l,{searchParams:t,accessToken:s,showSSOBanner:a}=e,[o]=er.Z.useForm(),[i]=er.Z.useForm(),{Title:c,Paragraph:d}=eR.default,[m,u]=(0,n.useState)(""),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)(!1),[f,y]=(0,n.useState)(!1),[Z,w]=(0,n.useState)(!1),[b,_]=(0,n.useState)(!1),[k,v]=(0,n.useState)(!1);try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let S=()=>{v(!1)},A=["proxy_admin","proxy_admin_viewer"];(0,n.useEffect)(()=>{(async()=>{if(null!=s){let e=[],l=await M(s,"proxy_admin_viewer");l.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(l));let t=await M(s,"proxy_admin");t.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(t)),console.log("combinedList: ".concat(e)),p(e)}})()},[s]);let N=()=>{w(!1),i.resetFields()},C=()=>{w(!1),i.resetFields()},I=e=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]}),P=(e,l,t)=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,r.jsx)(eA.Z,{value:l,children:A.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:t,valuePropName:"user_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t,disabled:!0})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Update role"})})]}),T=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,null);console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),h.ZP.success("Refresh tab to see updated user role"),w(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),g(!1)}}catch(e){console.error("Error creating the key:",e)}},F=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call"),e.user_email,e.user_id;let l=await B(s,e,"proxy_admin");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),y(!1)}}catch(e){console.error("Error creating the key:",e)}},O=async e=>{null!=s&&G(s,{environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}})};return console.log("admins: ".concat(null==x?void 0:x.length)),(0,r.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,r.jsx)(c,{level:4,children:"Admin Access "}),(0,r.jsxs)(d,{children:[a&&(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc. ",(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:x?x.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.user_role}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>w(!0)}),(0,r.jsx)(en.Z,{title:"Update role",visible:Z,width:800,footer:null,onOk:N,onCancel:C,children:P(T,e.user_role,e.user_id)})]})]},l)):null})]})})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("div",{className:"flex justify-start",children:[(0,r.jsx)(H.Z,{className:"mr-4 mb-5",onClick:()=>y(!0),children:"+ Add admin"}),(0,r.jsx)(en.Z,{title:"Add admin",visible:f,width:800,footer:null,onOk:()=>{y(!1),i.resetFields()},onCancel:()=>{y(!1),i.resetFields()},children:I(F)}),(0,r.jsx)(H.Z,{className:"mb-5",onClick:()=>g(!0),children:"+ Add viewer"}),(0,r.jsx)(en.Z,{title:"Add viewer",visible:j,width:800,footer:null,onOk:()=>{g(!1),i.resetFields()},onCancel:()=>{g(!1),i.resetFields()},children:I(E)})]})})]}),(0,r.jsxs)(Y.Z,{children:[(0,r.jsx)(c,{level:4,children:"Add SSO"}),(0,r.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,r.jsx)(H.Z,{onClick:()=>_(!0),children:"Add SSO"}),(0,r.jsx)(en.Z,{title:"Add SSO",visible:b,width:800,footer:null,onOk:()=>{_(!1),o.resetFields()},onCancel:()=>{_(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{F(e),O(e),_(!1),v(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,r.jsx)(eo.Z.Password,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,r.jsx)(eo.Z.Password,{})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,r.jsxs)(en.Z,{title:"SSO Setup Instructions",visible:k,width:800,footer:null,onOk:S,onCancel:()=>{v(!1)},children:[(0,r.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{onClick:S,children:"Done"})})]})]}),(0,r.jsxs)(e9.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access ",(0,r.jsxs)("a",{href:l,target:"_blank",children:[(0,r.jsx)("b",{children:l})," "]})]})]})]})},ll=t(12224),lt=e=>{let{accessToken:l,userRole:t,userID:s}=e,[a,o]=(0,n.useState)([]),[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)(!1),[u]=er.Z.useForm(),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)(""),[Z,w]=(0,n.useState)({}),[b,_]=(0,n.useState)([]),k=e=>{b.includes(e)?_(b.filter(l=>l!==e)):_([...b,e])},v={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),o(e.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 t=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",t),_(e.active_alerts),y(t),w(e.alerts_to_webhook)}c(l)})},[l,t,s]);let S=e=>b&&b.includes(e),A=e=>{if(!l)return;let t=Object.fromEntries(Object.entries(e.variables).map(e=>{var l;let[t,s]=e;return[t,(null===(l=document.querySelector('input[name="'.concat(t,'"]')))||void 0===l?void 0:l.value)||s]}));console.log("updatedVariables",t),console.log("updateAlertTypes",j);try{G(l,{environment_variables:t})}catch(e){h.ZP.error("Failed to update callback: "+e,20)}h.ZP.success("Callback updated successfully")},N=()=>{l&&u.validateFields().then(e=>{if(console.log("Form values:",e),"langfuse"===e.callback){G(l,{environment_variables:{LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey},litellm_settings:{success_callback:[e.callback]}});let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:null,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey}};o(a?[...a,t]:[t])}else if("slack"===e.callback){console.log("values.slackWebhookUrl: ".concat(e.slackWebhookUrl)),G(l,{general_settings:{alerting:["slack"],alerting_threshold:300},environment_variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl}}),console.log("values.callback: ".concat(e.callback));let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:null,LANGFUSE_SECRET_KEY:null}};o(a?[...a,t]:[t])}m(!1),u.resetFields(),p(null)})};return l?(console.log("callbacks: ".concat(a)),(0,r.jsxs)("div",{className:"w-full mx-4",children:[(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Logging Callbacks"}),(0,r.jsx)(eU.Z,{value:"2",children:"Alerting"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Callback"}),(0,r.jsx)(ev.Z,{children:"Callback Env Vars"})]})}),(0,r.jsx)(eb.Z,{children:a.map((e,t)=>{var s;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ep.Z,{color:"emerald",children:e.name})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)("ul",{children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).filter(e=>{let[l,t]=e;return null!==t}).map(e=>{let[l,t]=e;return(0,r.jsxs)("li",{children:[(0,r.jsx)(el.Z,{className:"mt-2",children:l}),"LANGFUSE_HOST"===l?(0,r.jsx)("p",{children:"default value=https://cloud.langfuse.com"}):(0,r.jsx)("div",{}),(0,r.jsx)($.Z,{name:l,defaultValue:t,type:"password"})]},l)})}),(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>A(e),children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,e.name),className:"mx-2",children:"Test Callback"})]})]},t)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{console.log("Add callback clicked"),m(!0)},children:"Add Callback"})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(el.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from ",(0,r.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{children:"Slack Webhook URL"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(v).map((e,l)=>{let[t,s]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ll.Z,{id:"switch",name:"switch",checked:S(t),onChange:()=>k(t)})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:s})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:t,type:"password",defaultValue:Z&&Z[t]?Z[t]:f})})]},l)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(v).forEach(l=>{let[t,s]=l,a=document.querySelector('input[name="'.concat(t,'"]'));console.log("key",t),console.log("webhookInput",a);let r=(null==a?void 0:a.value)||"";console.log("newWebhookValue",r),e[t]=r}),console.log("updatedAlertToWebhooks",e);let t={general_settings:{alert_to_webhook_url:e,alert_types:b}};console.log("payload",t);try{G(l,t)}catch(e){h.ZP.error("Failed to update alerts: "+e,20)}h.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,"slack"),className:"mx-2",children:"Test Alerts"})]})})]})]})}),(0,r.jsx)(en.Z,{title:"Add Callback",visible:d,onOk:N,width:800,onCancel:()=>{m(!1),u.resetFields(),p(null)},footer:null,children:(0,r.jsxs)(er.Z,{form:u,layout:"vertical",onFinish:N,children:[(0,r.jsx)(er.Z.Item,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,r.jsx)(ea.default,{onChange:e=>{p(e)},children:(0,r.jsx)(ea.default.Option,{value:"langfuse",children:"langfuse"})})}),"langfuse"===x&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PUBLIC_KEY",name:"langfusePublicKey",rules:[{required:!0,message:"Please enter the public key"}],children:(0,r.jsx)($.Z,{type:"password"})}),(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PRIVATE_KEY",name:"langfusePrivateKey",rules:[{required:!0,message:"Please enter the private key"}],children:(0,r.jsx)($.Z,{type:"password"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:ls}=ea.default;var la=e=>{let{models:l,accessToken:t,routerSettings:s,setRouterSettings:a}=e,[o]=er.Z.useForm(),[i,c]=(0,n.useState)(!1),[d,m]=(0,n.useState)("");return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>c(!0),children:"+ Add Fallbacks"}),(0,r.jsx)(en.Z,{title:"Add Fallbacks",visible:i,width:800,footer:null,onOk:()=>{c(!1),o.resetFields()},onCancel:()=>{c(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{console.log(e);let{model_name:l,models:r}=e,n=[...s.fallbacks||[],{[l]:r}],i={...s,fallbacks:n};console.log(i);try{G(t,{router_settings:i}),a(i)}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully"),c(!1),o.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,r.jsx)(eA.Z,{defaultValue:d,children:l&&l.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsx)(eK.Z,{value:l,children:l&&l.filter(e=>e!=d).map(e=>(0,r.jsx)(eV.Z,{value:e,children:e},e))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},lr=t(12968);async function ln(e,l){console.log("isLocal:",!1);let t=window.location.origin,s=new lr.ZP.OpenAI({apiKey:l,baseURL:t,dangerouslyAllowBrowser:!0});try{let l=await s.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});h.ZP.success((0,r.jsxs)("span",{children:["Test model=",(0,r.jsx)("strong",{children:e}),", received model=",(0,r.jsx)("strong",{children:l.model}),". See ",(0,r.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lo=e=>{let{accessToken:l,userRole:t,userID:s,modelData:a}=e,[o,i]=(0,n.useState)({}),[c,d]=(0,n.useState)(!1),[m]=er.Z.useForm(),[u,x]=(0,n.useState)(null),p={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"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),i(e.router_settings)})},[l,t,s]);let j=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(o.fallbacks)),o.fallbacks.map(l=>(e in l&&delete l[e],l));try{await G(l,{router_settings:o}),i({...o}),h.ZP.success("Router settings updated successfully")}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}}},g=e=>{if(!l)return;console.log("router_settings",e);let t=Object.fromEntries(Object.entries(e).map(e=>{let[l,t]=e;if("routing_strategy_args"!==l){var s;return[l,(null===(s=document.querySelector('input[name="'.concat(l,'"]')))||void 0===s?void 0:s.value)||t]}return null}).filter(e=>null!==e));console.log("updatedVariables",t);try{G(l,{router_settings:t})}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully")};return l?(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"General Settings"}),(0,r.jsx)(eU.Z,{value:"2",children:"Fallbacks"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,r.jsx)(et.Z,{children:"Router Settings"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Setting"}),(0,r.jsx)(ev.Z,{children:"Value"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(o).filter(e=>{let[l,t]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l}).map(e=>{let[l,t]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(el.Z,{children:l}),(0,r.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:p[l]})]}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:l,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t.toString()})})]},l)})})]})}),(0,r.jsx)(J.Z,{children:(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>g(o),children:"Save Changes"})})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Model Name"}),(0,r.jsx)(ev.Z,{children:"Fallbacks"})]})}),(0,r.jsx)(eb.Z,{children:o.fallbacks&&o.fallbacks.map((e,t)=>Object.entries(e).map(e=>{let[s,a]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:s}),(0,r.jsx)(e_.Z,{children:Array.isArray(a)?a.join(", "):a}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(H.Z,{onClick:()=>ln(s,l),children:"Test Fallback"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(eZ.Z,{icon:ex.Z,size:"sm",onClick:()=>j(s)})})]},t.toString()+s)}))})]}),(0,r.jsx)(la,{models:(null==a?void 0:a.data)?a.data.map(e=>e.model_name):[],accessToken:l,routerSettings:o,setRouterSettings:i})]})]})]})}):null},li=t(67951),lc=e=>{let{}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsxs)("div",{className:"mb-5",children:[(0,r.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,r.jsx)(el.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,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{children:[(0,r.jsx)(eU.Z,{children:"OpenAI Python SDK"}),(0,r.jsx)(eU.Z,{children:"LlamaIndex"}),(0,r.jsx)(eU.Z,{children:"Langchain Py"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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 = "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 ld(e,l,t,s){console.log("isLocal:",!1);let a=window.location.origin,r=new lr.ZP.OpenAI({apiKey:s,baseURL:a,dangerouslyAllowBrowser:!0});try{for await(let s of(await r.chat.completions.create({model:t,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&l(s.choices[0].delta.content)}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lm=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)(""),[m,h]=(0,n.useState)([]),[u,x]=(0,n.useState)(void 0),[p,j]=(0,n.useState)([]);(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{let e=await v(l,a,s);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}));console.log(l),j(l),x(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,a,s]);let g=(e,l)=>{h(t=>{let s=t[t.length-1];return s&&s.role===e?[...t.slice(0,t.length-1),{role:e,content:s.content+l}]:[...t,{role:e,content:l}]})},f=async()=>{if(""!==c.trim()&&o&&t&&s&&a){h(e=>[...e,{role:"user",content:c}]);try{u&&await ld(c,e=>g("assistant",e),u,o)}catch(e){console.error("Error fetching model response",e),g("assistant","Error fetching model response")}d("")}};if(s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,r.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsx)(ez.Z,{children:(0,r.jsx)(eU.Z,{children:"Chat"})}),(0,r.jsx)(eq.Z,{children:(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)("div",{className:"sm:max-w-2xl",children:(0,r.jsxs)(Y.Z,{numItems:2,children:[(0,r.jsxs)(J.Z,{children:[(0,r.jsx)(el.Z,{children:"API Key"}),(0,r.jsx)($.Z,{placeholder:"Type API Key here",type:"password",onValueChange:i,value:o})]}),(0,r.jsxs)(J.Z,{className:"mx-2",children:[(0,r.jsx)(el.Z,{children:"Select Model:"}),(0,r.jsx)(ea.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),x(e)},options:p,style:{width:"200px"}})]})]})}),(0,r.jsxs)(ew.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,r.jsx)(ek.Z,{children:(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{})})}),(0,r.jsx)(eb.Z,{children:m.map((e,l)=>(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,r.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)($.Z,{type:"text",value:c,onChange:e=>d(e.target.value),placeholder:"Type your message..."}),(0,r.jsx)(H.Z,{onClick:f,className:"ml-2",children:"Send"})]})})]})})]})})})})},lh=t(33509),lu=t(95781);let{Sider:lx}=lh.default;var lp=e=>{let{setPage:l,userRole:t,defaultSelectedKey:s}=e;return"Admin Viewer"==t?(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,r.jsx)(lx,{width:120,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["4"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:"API Keys"},"4"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:"Models"},"2"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:"Chat UI"},"3"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1")]})})}):(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,r.jsx)(lx,{width:145,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["1"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:(0,r.jsx)(el.Z,{children:"API Keys"})},"1"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:(0,r.jsx)(el.Z,{children:"Test Key"})},"3"),"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:(0,r.jsx)(el.Z,{children:"Models"})},"2"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:(0,r.jsx)(el.Z,{children:"Usage"})},"4"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("teams"),children:(0,r.jsx)(el.Z,{children:"Teams"})},"6"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("users"),children:(0,r.jsx)(el.Z,{children:"Users"})},"5"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("settings"),children:(0,r.jsx)(el.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("general-settings"),children:(0,r.jsx)(el.Z,{children:"Router Settings"})},"9"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("admin-panel"),children:(0,r.jsx)(el.Z,{children:"Admin"})},"7"):null,(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api_ref"),children:(0,r.jsx)(el.Z,{children:"API Reference"})},"11")]})})})},lj=t(67989),lg=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)([]),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)([]),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),[b,v]=(0,n.useState)([]),[S,T]=(0,n.useState)([]),[O,M]=(0,n.useState)([]),[R,L]=(0,n.useState)([]),[U,D]=(0,n.useState)(null),z=new Date(o.getFullYear(),o.getMonth(),1),B=new Date(o.getFullYear(),o.getMonth()+1,0),q=V(z),K=V(B);function V(e){let l=e.getFullYear(),t=e.getMonth()+1,s=e.getDate();return"".concat(l,"-").concat(t<10?"0"+t:t,"-").concat(s<10?"0"+s:s)}console.log("Start date is ".concat(q)),console.log("End date is ".concat(K)),(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{if(console.log("user role: ".concat(s)),"Admin"==s||"Admin Viewer"==s){let e=await I(l);c(e);let t=(await P(l)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,10),spend:e.total_spend}));m(t);let r=(await E(l)).map(e=>({key:e.model,spend:e.total_spend}));u(r);let n=await A(l);console.log("teamSpend",n),g(n.daily_spend),w(n.teams);let o=n.total_spend_per_team;o=o.map(e=>(e.name=e.team_id||"",e.value=e.total_spend||0,e)),v(o);let i=await N(l);y(i.top_10_tags);let d=(await _(l,a,s)).data;console.log("model groups in model dashboard",d);let h=[];for(let e=0;e{if(console.log("result from spend logs call",e),"daily_spend"in e){let l=e.daily_spend;console.log("daily spend",l),c(l);let t=e.top_api_keys;m(t)}else{let t=(await F(l,function(e){let l=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[t,s]=e;"spend"!==t&&"startTime"!==t&&"models"!==t&&"users"!==t&&l.push({key:t,spend:s})})}),l.sort((e,l)=>Number(l.spend)-Number(e.spend));let t=l.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(t[0]))),t}(e))).info.map(e=>({key:(e.key_name||e.key_alias).substring(0,10),spend:e.spend}));m(t),p(function(e){let l={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[t,s]=e;""!==t&&null!=t&&"None"!=t&&(l[t]||(l[t]=0),l[t]+=s)})});let t=Object.entries(l).map(e=>{let[l,t]=e;return{user_id:l,spend:t}});t.sort((e,l)=>l.spend-e.spend);let s=t.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)}});let e=await k(l,a,s,null);console.log("Model metrics response:",e);let r=[...e].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(e),M(r)}catch(e){console.error("There was an error fetching the data",e)}})()},[l,t,s,a,q,K]);let G=async e=>{if(console.log("Updating model metrics for group:",e),l&&a&&s){D(e);try{let t=await k(l,a,s,e);console.log("Model metrics response:",t);let r=[...t].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(t),M(r)}catch(e){console.error("Failed to fetch model metrics",e)}}};return(0,r.jsxs)("div",{style:{width:"100%"},className:"p-8",children:[(0,r.jsx)(eE,{userID:a,userRole:s,accessToken:l,userSpend:null,selectedTeam:null}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{className:"mt-2",children:[(0,r.jsx)(eU.Z,{children:"All Up"}),(0,r.jsx)(eU.Z,{children:"Team Based Usage"}),(0,r.jsx)(eU.Z,{children:"Tag Based Usage"}),(0,r.jsx)(eU.Z,{children:"Model Based Usage"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Monthly Spend"}),(0,r.jsx)(ej.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top API Keys"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Users"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Models"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:h,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsxs)(J.Z,{numColSpan:2,children:[(0,r.jsxs)(eg.Z,{className:"mb-2",children:[(0,r.jsx)(et.Z,{children:"Total Spend Per Team"}),(0,r.jsx)(lj.Z,{data:b})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Daily Spend Per Team"}),(0,r.jsx)(ej.Z,{className:"h-72",data:j,showLegend:!0,index:"date",categories:Z,yAxisWidth:80,colors:["blue","green","yellow","red","purple"],stack:!0})]})]}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Spend Per Tag - Last 30 Days"}),(0,r.jsxs)(el.Z,{children:["Get Started Tracking cost per tag ",(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/enterprise#tracking-spend-for-custom-tags",target:"_blank",children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Tag"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Requests"})]})}),(0,r.jsx)(eb.Z,{children:f.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.name}),(0,r.jsx)(e_.Z,{children:e.value}),(0,r.jsx)(e_.Z,{children:e.log_count})]},e.name))})]})]})}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)(et.Z,{children:"Filter By Model Group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080"},children:"View how requests were load balanced within a model group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080",fontStyle:"italic"},children:"(Beta feature) only supported for Azure Model Groups"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2",defaultValue:"all",children:[(0,r.jsx)(eN.Z,{value:"all",onClick:()=>G(null),children:"All Model Groups"}),R.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>G(e),children:e},l))]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Number Requests per Model"}),(0,r.jsx)(ej.Z,{data:S,className:"h-[50vh]",index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,r.jsxs)(eg.Z,{className:"mt-4",children:[(0,r.jsx)(et.Z,{children:"Latency Per Model"}),(0,r.jsx)(ej.Z,{data:O,className:"h-[50vh]",index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})]})]})]})},lf=()=>{let{Title:e,Paragraph:l}=eR.default,[t,s]=(0,n.useState)(""),[a,i]=(0,n.useState)(null),[c,d]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(!0),j=(0,o.useSearchParams)(),[g,f]=(0,n.useState)({data:[]}),y=j.get("userID"),Z=j.get("token"),[w,b]=(0,n.useState)("api-keys"),[_,k]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(Z){let e=(0,eM.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(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"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),s(l),"Admin Viewer"==l&&b("usage")}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[Z]),(0,r.jsx)(n.Suspense,{fallback:(0,r.jsx)("div",{children:"Loading..."}),children:(0,r.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,r.jsx)(m,{userID:y,userRole:t,userEmail:a,showSSOBanner:x}),(0,r.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,r.jsx)("div",{className:"mt-8",children:(0,r.jsx)(lp,{setPage:b,userRole:t,defaultSelectedKey:null})}),"api-keys"==w?(0,r.jsx)(eL,{userID:y,userRole:t,teams:c,keys:h,setUserRole:s,userEmail:a,setUserEmail:i,setTeams:d,setKeys:u}):"models"==w?(0,r.jsx)(e8,{userID:y,userRole:t,token:Z,accessToken:_,modelData:g,setModelData:f}):"llm-playground"==w?(0,r.jsx)(lm,{userID:y,userRole:t,token:Z,accessToken:_}):"users"==w?(0,r.jsx)(e6,{userID:y,userRole:t,token:Z,keys:h,teams:c,accessToken:_,setKeys:u}):"teams"==w?(0,r.jsx)(e7,{teams:c,setTeams:d,searchParams:j,accessToken:_,userID:y,userRole:t}):"admin-panel"==w?(0,r.jsx)(le,{setTeams:d,searchParams:j,accessToken:_,showSSOBanner:x}):"api_ref"==w?(0,r.jsx)(lc,{}):"settings"==w?(0,r.jsx)(lt,{userID:y,userRole:t,accessToken:_}):"general-settings"==w?(0,r.jsx)(lo,{userID:y,userRole:t,accessToken:_,modelData:g}):(0,r.jsx)(lg,{userID:y,userRole:t,token:Z,accessToken:_})]})]})})}}},function(e){e.O(0,[447,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-781ca5f151d78d1d.js b/litellm/proxy/_experimental/out/_next/static/chunks/app/page-781ca5f151d78d1d.js deleted file mode 100644 index 40715cd067c..00000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/app/page-781ca5f151d78d1d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,t){Promise.resolve().then(t.bind(t,27125))},27125:function(e,l,t){"use strict";t.r(l),t.d(l,{default:function(){return lf}});var s,a,r=t(3827),n=t(64090),o=t(47907),i=t(8792),c=t(40491),d=t(65270),m=e=>{let{userID:l,userRole:t,userEmail:s,showSSOBanner:a}=e;console.log("User ID:",l),console.log("userEmail:",s),console.log("showSSOBanner:",a);let n=[{key:"1",label:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["Role: ",t]}),(0,r.jsxs)("p",{children:["ID: ",l]})]})}];return(0,r.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,r.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,r.jsx)("div",{className:"flex flex-col items-center",children:(0,r.jsx)(i.default,{href:"/",children:(0,r.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,r.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,r.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,r.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,r.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Request hosted proxy"})}):null,(0,r.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,r.jsx)(c.Z,{menu:{items:n},children:(0,r.jsx)(d.Z,{children:s})})})]})]})},h=t(80588);let u=async()=>{try{let e=await fetch("https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"),l=await e.json();return console.log("received data: ".concat(l)),l}catch(e){throw console.error("Failed to get model cost map:",e),e}},x=async(e,l)=>{try{let t=await fetch("/model/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model created successfully. Wait 60s and refresh on 'All Models' page"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,l)=>{console.log("model_id in model delete call: ".concat(l));try{let t=await fetch("/model/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model deleted successfully. Restart server to see this."),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,l)=>{try{console.log("in keyDeleteCall:",l);let t=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,l)=>{try{console.log("in teamDeleteCall:",l);let t=await fetch("/team/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete team: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to delete key:",e),e}},Z=async function(e,l,t){let s=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,r=arguments.length>5?arguments[5]:void 0;try{let n="/user/info";"App Owner"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),"App User"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),console.log("in userInfoCall viewAll=",s),s&&r&&null!=a&&void 0!=a&&(n="".concat(n,"?view_all=true&page=").concat(a,"&page_size=").concat(r));let o=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let i=await o.json();return console.log("API Response:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},w=async(e,l)=>{try{let t="/team/info";l&&(t="".concat(t,"?team_id=").concat(l)),console.log("in teamInfoCall");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let l=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,l,t)=>{try{let l=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log("modelInfoCall:",t),t}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,l,t,s)=>{try{let l="/model/metrics";s&&(l="".concat(l,"?_selected_model_group=").concat(s));let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,l,t)=>{try{let l=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,l)=>{try{let t="/global/spend/logs";console.log("in keySpendLogsCall:",t);let s=await fetch("".concat(t,"?api_key=").concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let l="/global/spend/teams";console.log("in teamSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let l="/global/spend/tags";console.log("in tagsSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},C=async(e,l,t,s,a,r)=>{try{console.log("user role in spend logs call: ".concat(t));let l="/spend/logs";l="App Owner"==t?"".concat(l,"?user_id=").concat(s,"&start_date=").concat(a,"&end_date=").concat(r):"".concat(l,"?start_date=").concat(a,"&end_date=").concat(r);let n=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},I=async e=>{try{let l=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{let l=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,l)=>{try{l&&JSON.stringify({api_key:l});let t={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};l&&(t.body=JSON.stringify({api_key:l}));let s=await fetch("/global/spend/end_users",t);if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},E=async e=>{try{let l=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},F=async(e,l)=>{try{let t=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},O=async e=>{try{let l="/user/get_requests";console.log("in userGetRequesedtModelsCall:",l);let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to get requested models:",e),e}},M=async(e,l)=>{try{let t="/user/get_users?role=".concat(l);console.log("in userGetAllUsersCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},R=async(e,l)=>{try{console.log("Form Values in teamCreateCall:",l);let t=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,l)=>{try{console.log("Form Values in keyUpdateCall:",l);let t=await fetch("/key/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update key Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,l)=>{try{console.log("Form Values in teamUpateCall:",l);let t=await fetch("/team/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update team: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update Team Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async(e,l)=>{try{console.log("Form Values in modelUpateCall:",l);let t=await fetch("/model/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update model: "+e,20),console.error("Error update from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update model Response:",s),s}catch(e){throw console.error("Failed to update model:",e),e}},z=async(e,l,t)=>{try{console.log("Form Values in teamMemberAddCall:",t);let s=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:l,member:t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,l,t)=>{try{console.log("Form Values in userUpdateUserCall:",l);let s={...l};null!==t&&(s.user_role=t),s=JSON.stringify(s);let a=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:s});if(!a.ok){let e=await a.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await a.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,l)=>{try{let t=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,l)=>{try{let t="/health/services?service=".concat(l);console.log("Checking Slack Budget Alerts service health");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed ".concat(l," service health check ")+e),Error(e)}let a=await s.json();return h.ZP.success("Test request to ".concat(l," made - check logs/alerts on ").concat(l," to verify")),a}catch(e){throw console.error("Failed to perform health check:",e),e}},V=async(e,l,t)=>{try{let l=await fetch("/get/config/callbacks",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},G=async(e,l)=>{try{let t=await fetch("/config/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},W=async e=>{try{let l=await fetch("/health",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to call /health:",e),e}};var J=t(10384),Y=t(46453),H=t(2179),$=t(52273),X=t(26780),Q=t(15595),ee=t(6698),el=t(71801),et=t(42440),es=t(42308),ea=t(50670),er=t(81583),en=t(99129),eo=t(44839),ei=t(88707),ec=t(1861);let{Option:ed}=ea.default;var em=e=>{let{userID:l,team:t,userRole:s,accessToken:a,data:o,setData:i}=e,[c]=er.Z.useForm(),[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(null),[p,g]=(0,n.useState)(null),[f,y]=(0,n.useState)([]),Z=()=>{m(!1),c.resetFields()},w=()=>{m(!1),x(null),c.resetFields()};(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==a){let e=(await v(a,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),y(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,s]);let b=async e=>{try{h.ZP.info("Making API Call"),m(!0);let t=await j(a,l,e);console.log("key create Response:",t),i(e=>e?[...e,t]:[t]),x(t.key),g(t.soft_budget),h.ZP.success("API Key Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,r.jsx)(en.Z,{title:"Create Key",visible:d,width:800,footer:null,onOk:Z,onCancel:w,children:(0,r.jsxs)(er.Z,{form:c,onFinish:b,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:t?t.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t?t.team_alias:"",disabled:!0})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&c.setFieldsValue({models:["all-team-models"]})},children:[(0,r.jsx)(ed,{value:"all-team-models",children:"All Team Models"},"all-team-models"),t&&t.models?t.models.includes("all-proxy-models")?f.map(e=>(0,r.jsx)(ed,{value:e,children:e},e)):t.models.map(e=>(0,r.jsx)(ed,{value:e,children:e},e)):f.map(e=>(0,r.jsx)(ed,{value:e,children:e},e))]})}),(0,r.jsxs)(X.Z,{className:"mt-20 mb-8",children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)("b",{children:"Optional Settings"})}),(0,r.jsxs)(Q.Z,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==t?void 0:t.max_budget)!==null&&(null==t?void 0:t.max_budget)!==void 0?null==t?void 0:t.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.max_budget&&l>t.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(t.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==t?void 0:t.budget_duration)!==null&&(null==t?void 0:t.budget_duration)!==void 0?null==t?void 0:t.budget_duration:"None"),children:(0,r.jsxs)(ea.default,{defaultValue:null,placeholder:"n/a",children:[(0,r.jsx)(ea.default.Option,{value:"24h",children:"daily"}),(0,r.jsx)(ea.default.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==t?void 0:t.tpm_limit)!==null&&(null==t?void 0:t.tpm_limit)!==void 0?null==t?void 0:t.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.tpm_limit&&l>t.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(t.tpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==t?void 0:t.rpm_limit)!==null&&(null==t?void 0:t.rpm_limit)!==void 0?null==t?void 0:t.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.rpm_limit&&l>t.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(t.rpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Key"})})]})}),u&&(0,r.jsx)(en.Z,{visible:d,onOk:Z,onCancel:w,footer:null,children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Save your Key"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,r.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,r.jsx)(J.Z,{numColSpan:1,children:null!=u?(0,r.jsxs)("div",{children:[(0,r.jsx)(el.Z,{className:"mt-3",children:"API Key:"}),(0,r.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,r.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:u})}),(0,r.jsx)(es.CopyToClipboard,{text:u,onCopy:()=>{h.ZP.success("API Key copied to clipboard")},children:(0,r.jsx)(H.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,r.jsx)(el.Z,{children:"Key being created, this might take 30s"})})]})})]})},eh=t(9454),eu=t(98941),ex=t(33393),ep=t(5),ej=t(9853),eg=t(13810),ef=t(39290),ey=t(66952),eZ=t(61244),ew=t(10827),eb=t(3851),e_=t(2044),ek=t(64167),ev=t(74480),eS=t(7178),eA=t(95093),eN=t(27166);let{Option:eC}=ea.default;var eI=e=>{let{userID:l,userRole:t,accessToken:s,selectedTeam:a,data:o,setData:i,teams:c}=e,[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(!1),[p,j]=(0,n.useState)(null),[g,y]=n.useState(null),[Z,w]=(0,n.useState)(null),[b,_]=(0,n.useState)(null),[k,A]=(0,n.useState)(""),[N,C]=(0,n.useState)(!1),[I,P]=(0,n.useState)(null),[T,E]=(0,n.useState)([]),F=new Set,[O,M]=(0,n.useState)(F);(0,n.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==s&&null!==t){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),E(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[s,l,t]),(0,n.useEffect)(()=>{if(c){let e=new Set;c.forEach((l,t)=>{let s=l.team_id;e.add(s)}),M(e)}},[c]);let R=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),P(e),C(!0)},U=async e=>{if(null==s)return;let l=e.token;e.key=l,console.log("handleEditSubmit:",e);let t=await L(s,e);console.log("handleEditSubmit: newKeyValues",t),o&&i(o.map(e=>e.token===l?t:e)),h.ZP.success("Key updated successfully"),C(!1),P(null)},D=async e=>{try{if(null==s||null==e)return;console.log("accessToken: ".concat(s,"; token: ").concat(e.token));let l=await S(s,e.token);console.log("Response:",l),_(l);try{let e=await q(s,l);console.log("Response2:",e);let t=[...l,...e.response];_(t),A(e.predicted_spend),console.log("Combined Data:",t)}catch(e){console.error("There was an error fetching the predicted data",e)}}catch(e){console.error("There was an error fetching the data",e)}};(0,n.useEffect)(()=>{D(Z)},[Z]);let z=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=o&&(j(e.token),localStorage.removeItem("userData"+l),x(!0))},B=async()=>{if(null!=p&&null!=o){try{await f(s,p);let e=o.filter(e=>e.token!==p);i(e)}catch(e){console.error("Error deleting the key:",e)}x(!1),j(null)}};if(null!=o)return console.log("RERENDER TRIGGERED"),(0,r.jsxs)("div",{children:[(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,r.jsxs)(ew.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Key Alias"}),(0,r.jsx)(ev.Z,{children:"Secret Key"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"})]})}),(0,r.jsx)(eb.Z,{children:o.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(a){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(O.has(e.team_id),", selectedTeam id: ").concat(a.team_id)),(null!=a.team_id||null===e.team_id||O.has(e.team_id))&&e.team_id!=a.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,r.jsx)(el.Z,{children:e.key_alias}):(0,r.jsx)(el.Z,{children:"Not Set"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.key_name})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,r.jsx)(e_.Z,{children:null!=e.max_budget?(0,r.jsx)(el.Z,{children:e.max_budget}):(0,r.jsx)(el.Z,{children:"Unlimited"})}),(0,r.jsx)(e_.Z,{children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(r.Fragment,{children:a&&a.models&&a.models.length>0?a.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(el.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{onClick:()=>{w(e),y(e.id)},icon:eh.Z,size:"sm"}),(0,r.jsx)(ef.Z,{open:null!==g,onClose:()=>{y(null),w(null)},children:(0,r.jsx)(ey.Z,{children:Z&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",children:[(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(Z.spend).toFixed(4)}catch(e){return Z.spend}})()})})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.max_budget?(0,r.jsx)(r.Fragment,{children:Z.max_budget}):(0,r.jsx)(r.Fragment,{children:"Unlimited"})})})]},e.name),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.expires?(0,r.jsx)(r.Fragment,{children:new Date(Z.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,r.jsx)(r.Fragment,{children:"Never"})})})]},e.name)]}),(0,r.jsx)(eg.Z,{className:"mt-6 mb-6",children:b&&(0,r.jsx)(ej.Z,{className:"mt-6",data:b,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})}),(0,r.jsx)(et.Z,{children:"Metadata"}),(0,r.jsx)(el.Z,{children:JSON.stringify(Z.metadata)}),(0,r.jsx)(H.Z,{variant:"light",className:"mx-auto flex items-center",onClick:()=>{y(null),w(null)},children:"Close"})]})})}),(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>R(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>z(e),icon:ex.Z,size:"sm"})]})]},e.token)})})]}),u&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:B,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{x(!1),j(null)},children:"Cancel"})]})]})]})})]}),I&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,token:s,onSubmit:o}=e,[i]=er.Z.useForm(),[d,m]=(0,n.useState)(a),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)(!1);return(0,r.jsx)(en.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{i.validateFields().then(e=>{i.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:i,onFinish:U,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let t=l.filter(e=>!d.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!d.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(t)),t.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(t,"Team models: ").concat(d.models)):Promise.resolve()}}],children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(eC,{value:"all-team-models",children:"All Team Models"},"all-team-models"),d&&d.models?d.models.includes("all-proxy-models")?T.filter(e=>"all-proxy-models"!==e).map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):d.models.map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):T.map(e=>(0,r.jsx)(eC,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==d?void 0:d.max_budget)!==null&&(null==d?void 0:d.max_budget)!==void 0?null==d?void 0:d.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&d&&null!==d.max_budget&&l>d.max_budget)throw console.log("keyTeam.max_budget: ".concat(d.max_budget)),Error("Budget cannot exceed team max budget: $".concat(d.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"token",name:"token",hidden:!0}),(0,r.jsx)(er.Z.Item,{label:"Team",name:"team_id",help:"the team this key belongs to",children:(0,r.jsx)(eA.Z,{value:s.team_alias,children:null==c?void 0:c.map((e,l)=>(0,r.jsx)(eN.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:N,onCancel:()=>{C(!1),P(null)},token:I,onSubmit:U})]})},eP=t(76032),eT=t(35152),eE=e=>{let{userID:l,userRole:t,accessToken:s,userSpend:a,selectedTeam:o}=e;console.log("userSpend: ".concat(a));let[i,c]=(0,n.useState)(null!==a?a:0),[d,m]=(0,n.useState)(0),[h,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{let e=async()=>{if(s&&l&&t&&"Admin"===t&&null==a)try{let e=await b(s);e&&(e.spend?c(e.spend):c(0),e.max_budget?m(e.max_budget):m(0))}catch(e){console.error("Error fetching global spend data:",e)}};(async()=>{try{if(null===l||null===t)return;if(null!==s){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),u(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,s,l]),(0,n.useEffect)(()=>{null!==a&&c(a)},[a]);let x=[];o&&o.models&&(x=o.models),x&&x.includes("all-proxy-models")?(console.log("user models:",h),x=h):x&&x.includes("all-team-models")?x=o.models:x&&0===x.length&&(x=h);let p=void 0!==i?i.toFixed(4):null;return console.log("spend in view user spend: ".concat(i)),(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:["Total Spend"," "]}),(0,r.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",p]})]}),(0,r.jsx)("div",{className:"ml-auto",children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Team Models"})}),(0,r.jsx)(Q.Z,{className:"absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs",children:(0,r.jsx)(eP.Z,{children:x.map(e=>(0,r.jsx)(eT.Z,{children:(0,r.jsx)(el.Z,{children:e})},e))})})]})})]})},eF=e=>{let{userID:l,userRole:t,selectedTeam:s,accessToken:a}=e,[o,i]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==a){let e=(await v(a,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),i(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,t]);let c=[];return s&&s.models&&(c=s.models),c&&c.includes("all-proxy-models")&&(console.log("user models:",o),c=o),(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"mb-5",children:(0,r.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==s?void 0:s.team_alias})})})},eO=e=>{let l,{teams:t,setSelectedTeam:s,userRole:a}=e,o={models:[],team_id:null,team_alias:"Default Team"},[i,c]=(0,n.useState)(o);return(l="App User"===a?t:t?[...t,o]:[o],"App User"===a)?null:(0,r.jsxs)("div",{className:"mt-5 mb-5",children:[(0,r.jsx)(et.Z,{children:"Select Team"}),(0,r.jsx)(el.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,r.jsxs)(el.Z,{className:"mt-3 mb-3",children:[(0,r.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,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>s(e),children:e.team_alias},l))}):(0,r.jsxs)(el.Z,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]})},eM=t(37963),eR=t(36083);console.log("isLocal:",!1);var eL=e=>{let{userID:l,userRole:t,teams:s,keys:a,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:m,setKeys:h}=e,[u,x]=(0,n.useState)(null),p=(0,o.useSearchParams)();p.get("viewSpend"),(0,o.useRouter)();let j=p.get("token"),[g,f]=(0,n.useState)(null),[y,w]=(0,n.useState)(null),[_,k]=(0,n.useState)([]),S={models:[],team_alias:"Default Team",team_id:null},[A,N]=(0,n.useState)(s?s[0]:S);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(j){let e=(0,eM.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),i(l)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&g&&t&&!a&&!u){let e=sessionStorage.getItem("userModels"+l);e?k(JSON.parse(e)):(async()=>{try{let e=await Z(g,l,t,!1,null,null);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==t){let e=await b(g);x(e),console.log("globalSpend:",e)}else x(e.user_info);h(e.keys),m(e.teams);let s=[...e.teams];s.length>0?(console.log("response['teams']: ".concat(s)),N(s[0])):N(S),sessionStorage.setItem("userData"+l,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(e.user_info));let a=(await v(g,l,t)).data.map(e=>e.id);console.log("available_model_names:",a),k(a),console.log("userModels:",_),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,j,g,a,t]),(0,n.useEffect)(()=>{if(null!==a&&null!=A){let e=0;for(let l of a)A.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===A.team_id&&(e+=l.spend);w(e)}else if(null!==a){let e=0;for(let l of a)e+=l.spend;w(e)}},[A]),null==l||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==g)return null;if(null==t&&i("App Owner"),t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",A),console.log("teamSpend: ".concat(y)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eF,{userID:l,userRole:t,selectedTeam:A||null,accessToken:g}),(0,r.jsx)(eE,{userID:l,userRole:t,accessToken:g,userSpend:y,selectedTeam:A||null}),(0,r.jsx)(eI,{userID:l,userRole:t,accessToken:g,selectedTeam:A||null,data:a,setData:h,teams:s}),(0,r.jsx)(em,{userID:l,team:A||null,userRole:t,accessToken:g,data:a,setData:h},A?A.team_id:null),(0,r.jsx)(eO,{teams:s,setSelectedTeam:N,userRole:t})]})})})},eU=t(92836),eD=t(26734),ez=t(41608),eB=t(32126),eq=t(23682),eK=t(47047),eV=t(76628),eG=t(38302),eW=t(28683),eJ=t(1460),eY=t(78578),eH=t(63954),e$=e=>{let{modelID:l,accessToken:t}=e,[s,a]=(0,n.useState)(!1),o=async()=>{try{h.ZP.info("Making API Call"),a(!0);let e=await p(t,l);console.log("model delete Response:",e),h.ZP.success("Model ".concat(l," deleted successfully")),a(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(eZ.Z,{onClick:()=>a(!0),icon:ex.Z,size:"sm"}),(0,r.jsx)(en.Z,{open:s,onOk:o,okType:"danger",onCancel:()=>a(!1),children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Delete Model"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Model ID: ",(0,r.jsx)("b",{children:l})]})})]})})]})},eX=t(97766),eQ=t(46495);let{Title:e0,Link:e1}=eR.default;(s=a||(a={})).OpenAI="OpenAI",s.Azure="Azure",s.Anthropic="Anthropic",s.Google_AI_Studio="Gemini (Google AI Studio)",s.Bedrock="Amazon Bedrock",s.OpenAI_Compatible="OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)",s.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)";let e2={OpenAI:"openai",Azure:"azure",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai"},e4=async(e,l,t)=>{try{let s=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(s)),console.log("received type of deployments: ".concat(typeof s)),s.forEach(async t=>{console.log("litellm_model: ".concat(t));let s={},a={};s.model=t;let r="";for(let[l,t]of Object.entries(e))if(""!==t){if("model_name"==l)r+=t;else if("custom_llm_provider"==l)continue;else if("model"==l)continue;else if("base_model"===l)a[l]=t;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",t);let e={};if(t&&void 0!=t){try{e=JSON.parse(t)}catch(e){throw h.ZP.error("Failed to parse LiteLLM Extra Params: "+e,20),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,t]of Object.entries(e))s[l]=t}}else s[l]=t}let n={model_name:r,litellm_params:s,model_info:a},o=await x(l,n);console.log("response for model create call: ".concat(o.data))}),t.resetFields()}catch(e){h.ZP.error("Failed to create model: "+e,20)}};var e8=e=>{var l,t,s;let{accessToken:o,token:i,userRole:c,userID:d,modelData:m={data:[]},setModelData:x}=e,[p,j]=(0,n.useState)([]),[g]=er.Z.useForm(),[f,y]=(0,n.useState)(null),[Z,w]=(0,n.useState)(""),[b,k]=(0,n.useState)([]),v=Object.values(a).filter(e=>isNaN(Number(e))),[S,A]=(0,n.useState)("OpenAI"),[N,C]=(0,n.useState)(""),[I,P]=(0,n.useState)(!1),[T,E]=(0,n.useState)(null),[F,M]=(0,n.useState)([]),[R,L]=(0,n.useState)(null),U=e=>{E(e),P(!0)},z=async e=>{if(console.log("handleEditSubmit:",e),null==o)return;let l={},t=null;for(let[s,a]of Object.entries(e))"model_id"!==s?l[s]=a:t=a;let s={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",s),await D(o,s),h.ZP.success("Model updated successfully, restart server to see updates"),P(!1),E(null)},B=()=>{w(new Date().toLocaleString())};if((0,n.useEffect)(()=>{if(!o||!i||!c||!d)return;let e=async()=>{try{let e=await _(o,d,c);console.log("Model data response:",e.data),x(e);let l=new Set;for(let t=0;t{let e=await u();console.log("received model cost map data: ".concat(Object.keys(e))),y(e)};null==f&&l(),B()},[o,i,c,d,f,Z]),!m||!o||!i||!c||!d)return(0,r.jsx)("div",{children:"Loading..."});let q=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(f)),null!=f&&"object"==typeof f&&e in f)?f[e].litellm_provider:"openai";if(a){let e=a.split("/"),l=e[0];n=1===e.length?h(a):l}else n="openai";r&&(o=null==r?void 0:r.input_cost_per_token,i=null==r?void 0:r.output_cost_per_token,c=null==r?void 0:r.max_tokens),(null==s?void 0:s.litellm_params)&&(d=Object.fromEntries(Object.entries(null==s?void 0:s.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),m.data[e].provider=n,m.data[e].input_cost=o,m.data[e].output_cost=i,m.data[e].max_tokens=c,m.data[e].api_base=null==s?void 0:null===(t=s.litellm_params)||void 0===t?void 0:t.api_base,m.data[e].cleanedLitellmParams=d,q.push(s.model_name),console.log(m.data[e])}if(c&&"Admin Viewer"==c){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let K=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(a).find(l=>a[l]===e);if(l){let e=e2[l];console.log("mappingResult: ".concat(e));let t=[];"object"==typeof f&&Object.entries(f).forEach(l=>{let[s,a]=l;null!==a&&"object"==typeof a&&"litellm_provider"in a&&(a.litellm_provider===e||a.litellm_provider.includes(e))&&t.push(s)}),k(t),console.log("providerModels: ".concat(b))}},V=async()=>{try{h.ZP.info("Running health check..."),C("");let e=await W(o);C(e)}catch(e){console.error("Error running health check:",e),C("Error running health check")}};return console.log("selectedProvider: ".concat(S)),console.log("providerModels.length: ".concat(b.length)),(0,r.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)(eU.Z,{children:"All Models"}),(0,r.jsx)(eU.Z,{children:"Add Model"}),(0,r.jsx)(eU.Z,{children:(0,r.jsx)("pre",{children:"/health Models"})})]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[Z&&(0,r.jsxs)(el.Z,{children:["Last Refreshed: ",Z]}),(0,r.jsx)(eZ.Z,{icon:eH.Z,variant:"shadow",size:"xs",className:"self-center",onClick:B})]})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(Y.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(el.Z,{children:"Filter by Public Model Name"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:"all",onValueChange:e=>L("all"===e?"all":e),children:[(0,r.jsx)(eN.Z,{value:"all",children:"All Models"}),F.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>L(e),children:e},l))]})]}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Public Model Name "}),(0,r.jsx)(ev.Z,{children:"Provider"}),"Admin"===c&&(0,r.jsx)(ev.Z,{children:"API Base"}),(0,r.jsx)(ev.Z,{children:"Extra litellm Params"}),(0,r.jsx)(ev.Z,{children:"Input Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Output Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Max Tokens"})]})}),(0,r.jsx)(eb.Z,{children:m.data.filter(e=>"all"===R||e.model_name===R||null==R||""===R).map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.model_name})}),(0,r.jsx)(e_.Z,{children:e.provider}),"Admin"===c&&(0,r.jsx)(e_.Z,{children:e.api_base}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Litellm params"})}),(0,r.jsx)(Q.Z,{children:(0,r.jsx)("pre",{children:JSON.stringify(e.cleanedLitellmParams,null,2)})})]})}),(0,r.jsx)(e_.Z,{children:e.input_cost}),(0,r.jsx)(e_.Z,{children:e.output_cost}),(0,r.jsx)(e_.Z,{children:e.max_tokens}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>U(e)}),(0,r.jsx)(e$,{modelID:e.model_info.id,accessToken:o})]})]},l))})]})})]}),(0,r.jsx)(e=>{let{visible:l,onCancel:t,model:s,onSubmit:a}=e,[n]=er.Z.useForm(),o={},i="",c="";if(s){o=s.litellm_params,i=s.model_name;let e=s.model_info;e&&(c=e.id,console.log("model_id: ".concat(c)),o.model_id=c)}return(0,r.jsx)(en.Z,{title:"Edit Model "+i,visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a(e),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:z,initialValues:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,r.jsx)($.Z,{})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"max_retries",name:"max_retries",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:I,onCancel:()=>{P(!1),E(null)},model:T,onSubmit:z})]}),(0,r.jsxs)(eB.Z,{className:"h-full",children:[(0,r.jsx)(e0,{level:2,children:"Add new model"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(er.Z,{form:g,onFinish:()=>{g.validateFields().then(e=>{e4(e,o,g)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.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,r.jsx)(eA.Z,{value:S.toString(),children:v.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>{K(e),A(e)},children:e},l))})}),(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"Vertex AI (Anthropic, Gemini, etc.)"===(s=S.toString())?"gemini-pro":"Anthropic"==s?"claude-3-opus":"Amazon Bedrock"==s?"claude-3-opus":"Gemini (Google AI Studio)"==s?"gemini-pro":"gpt-3.5-turbo"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"LiteLLM Model Name(s)",name:"model",tooltip:"Actual model name used for making litellm.completion() call.",className:"mb-0",children:"Azure"===S?(0,r.jsx)($.Z,{placeholder:"Enter model name"}):b.length>0?(0,r.jsx)(eK.Z,{value:b,children:b.map((e,l)=>(0,r.jsx)(eV.Z,{value:e,children:e},l))}):(0,r.jsx)($.Z,{placeholder:"gpt-3.5-turbo-0125"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Actual model name used for making ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," models with the same 'public name'"]})})]}),"Amazon Bedrock"!=S&&"Vertex AI (Anthropic, Gemini, etc.)"!=S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,r.jsx)($.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==S&&(0,r.jsx)(er.Z.Item,{label:"Organization ID",name:"organization_id",children:(0,r.jsx)($.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,r.jsx)($.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,r.jsx)($.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,r.jsx)(eQ.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;g.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?h.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&h.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,r.jsx)(ec.ZP,{icon:(0,r.jsx)(eX.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==S||"OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)"==S)&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,r.jsx)($.Z,{placeholder:"https://..."})}),"Azure"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Version",name:"api_version",children:(0,r.jsx)($.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==S&&(0,r.jsxs)(er.Z.Item,{label:"Base Model",name:"base_model",children:[(0,r.jsx)($.Z,{placeholder:"azure/gpt-3.5-turbo"}),(0,r.jsxs)(el.Z,{children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from ",(0,r.jsx)(e1,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})]}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"us-east-1"})}),(0,r.jsx)(er.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,r.jsx)(eY.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,r.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Model"})}),(0,r.jsx)(eJ.Z,{title:"Get help on our github",children:(0,r.jsx)(eR.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(el.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,r.jsx)(H.Z,{onClick:V,children:"Run `/health`"}),N&&(0,r.jsx)("pre",{children:JSON.stringify(N,null,2)})]})})]})]})})};let{Option:e5}=ea.default;var e3=e=>{let{userID:l,accessToken:t,teams:s}=e,[a]=er.Z.useForm(),[o,i]=(0,n.useState)(!1),[c,d]=(0,n.useState)(null),[m,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await v(t,l,"any"),s=[];for(let l=0;l{i(!1),a.resetFields()},p=()=>{i(!1),d(null),a.resetFields()},j=async e=>{try{h.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let s=await g(t,null,e);console.log("user create Response:",s),d(s.key),h.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Invite User"}),(0,r.jsxs)(en.Z,{title:"Invite User",visible:o,width:800,footer:null,onOk:x,onCancel:p,children:[(0,r.jsx)(el.Z,{className:"mb-1",children:"Invite a user to login to the Admin UI and create Keys"}),(0,r.jsx)(el.Z,{className:"mb-6",children:(0,r.jsx)("b",{children:"Note: SSO Setup Required for this"})}),(0,r.jsxs)(er.Z,{form:a,onFinish:j,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsx)(er.Z.Item,{label:"User Email",name:"user_email",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",children:(0,r.jsx)(ea.default,{placeholder:"Select Team ID",style:{width:"100%"},children:s?s.map(e=>(0,r.jsx)(e5,{value:e.team_id,children:e.team_alias},e.team_id)):(0,r.jsx)(e5,{value:null,children:"Default Team"},"default")})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create User"})})]})]}),c&&(0,r.jsxs)(en.Z,{title:"User Created Successfully",visible:o,onOk:x,onCancel:p,footer:null,children:[(0,r.jsx)("p",{children:"User has been created to access your proxy. Please Ask them to Log In."}),(0,r.jsx)("br",{}),(0,r.jsx)("p",{children:(0,r.jsx)("b",{children:"Note: This Feature is only supported through SSO on the Admin UI"})})]})]})},e6=e=>{let{accessToken:l,token:t,keys:s,userRole:a,userID:o,teams:i,setKeys:c}=e,[d,m]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(0),[j,g]=n.useState(null),[f,y]=(0,n.useState)(null);if((0,n.useEffect)(()=>{if(!l||!t||!a||!o)return;let e=async()=>{try{let e=await Z(l,null,a,!0,x,25);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};l&&t&&a&&o&&e();let s=async()=>{try{let e=await T(l,null);console.log("user data response:",e),u(e)}catch(e){console.error("There was an error fetching the model data",e)}};a&&("Admin"==a||"Admin Viewer"==a)&&!h&&s()},[l,t,a,o,x]),!d||!l||!t||!a||!o)return(0,r.jsx)("div",{children:"Loading..."});let w=async e=>{try{let t=await T(l,e);console.log("user data response:",t),u(t)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,r.jsx)("div",{style:{width:"100%"},children:(0,r.jsxs)(Y.Z,{className:"gap-2 p-2 h-[80vh] w-full mt-8",children:[(0,r.jsx)(e3,{userID:o,accessToken:l,teams:i}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[80vh] mb-4",children:[(0,r.jsxs)("div",{className:"mb-4 mt-1",children:[(0,r.jsxs)(el.Z,{children:[(0,r.jsx)("b",{children:"Key Owners: "})," Users on LiteLLM that created API Keys. Automatically tracked by LiteLLM"]}),(0,r.jsxs)(el.Z,{className:"mt-1",children:[(0,r.jsx)("b",{children:"End Users: "}),"End Users of your LLM API calls. Tracked When a `user` param is passed in your LLM calls"]})]}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Key Owners"}),(0,r.jsx)(eU.Z,{value:"2",children:"End-Users"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"User ID"}),(0,r.jsx)(ev.Z,{children:"User Email"}),(0,r.jsx)(ev.Z,{children:"User Models"}),(0,r.jsx)(ev.Z,{children:"User Spend ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User Max Budget ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User API Key Aliases"})]})}),(0,r.jsx)(eb.Z,{children:d.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_id}),(0,r.jsx)(e_.Z,{children:e.user_email}),(0,r.jsx)(e_.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,r.jsx)(e_.Z,{children:e.spend?e.spend:0}),(0,r.jsx)(e_.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(Y.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,r.jsx)(ep.Z,{size:"xs",color:"indigo",children:e.key_aliases.filter(e=>null!==e).join(", ")}):(0,r.jsx)(ep.Z,{size:"xs",color:"gray",children:"No Keys"})})})]},e.user_id))})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)("div",{className:"flex-1"}),(0,r.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,r.jsx)(el.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,r.jsx)(eA.Z,{defaultValue:"1",className:"w-3/4",children:null==s?void 0:s.map((e,l)=>{if(e&&null!==e.key_alias&&e.key_alias.length>0)return(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>w(e.token),children:e.key_alias},l)})})]})]}),(0,r.jsxs)(ew.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"End User"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Total Events"})]})}),(0,r.jsx)(eb.Z,{children:null==h?void 0:h.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.end_user}),(0,r.jsx)(e_.Z,{children:e.total_spend}),(0,r.jsx)(e_.Z,{children:e.total_events})]},l))})]})]})]})]})]}),function(){if(!d)return null;let e=Math.ceil(d.length/25);return(0,r.jsxs)("div",{className:"flex justify-between items-center",children:[(0,r.jsxs)("div",{children:["Showing Page ",x+1," of ",e]}),(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:0===x,onClick:()=>p(x-1),children:"← Prev"}),(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",onClick:()=>{p(x+1)},children:"Next →"})]})]})}()]})})},e7=e=>{let{teams:l,searchParams:t,accessToken:s,setTeams:a,userID:o,userRole:i}=e,[c]=er.Z.useForm(),[d]=er.Z.useForm(),{Title:m,Paragraph:u}=eR.default,[x,p]=(0,n.useState)(""),[j,g]=(0,n.useState)(!1),[f,Z]=(0,n.useState)(l?l[0]:null),[b,_]=(0,n.useState)(!1),[k,S]=(0,n.useState)(!1),[A,N]=(0,n.useState)([]),[C,I]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),[E,F]=(0,n.useState)({}),O=e=>{Z(e),g(!0)},M=async e=>{let t=e.team_id;if(console.log("handleEditSubmit:",e),null==s)return;let r=await U(s,e);l&&a(l.map(e=>e.team_id===t?r.data:e)),h.ZP.success("Team updated successfully"),g(!1),Z(null)},L=async e=>{T(e),I(!0)},D=async()=>{if(null!=P&&null!=l&&null!=s){try{await y(s,P);let e=l.filter(e=>e.team_id!==P);a(e)}catch(e){console.error("Error deleting the team:",e)}I(!1),T(null)}};(0,n.useEffect)(()=>{let e=async()=>{try{if(null===o||null===i||null===s||null===l)return;console.log("fetching team info:");let e={};for(let t=0;t<(null==l?void 0:l.length);t++){let a=l[t].team_id,r=await w(s,a);console.log("teamInfo response:",r),null!==r&&(e={...e,[a]:r})}F(e)}catch(e){console.error("Error fetching team info:",e)}};(async()=>{try{if(null===o||null===i)return;if(null!==s){let e=(await v(s,o,i)).data.map(e=>e.id);console.log("available_model_names:",e),N(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,o,i,l]);let B=async e=>{try{if(null!=s){h.ZP.info("Creating Team");let t=await R(s,e);null!==l?a([...l,t]):a([t]),console.log("response for team create call: ".concat(t)),h.ZP.success("Team created"),_(!1)}}catch(e){console.error("Error creating the team:",e),h.ZP.error("Error creating the team: "+e,20)}},q=async e=>{try{if(null!=s&&null!=l){h.ZP.info("Adding Member");let t={role:"user",user_email:e.user_email,user_id:e.user_id},r=await z(s,f.team_id,t);console.log("response for team create call: ".concat(r.data));let n=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(r.data.team_id)),e.team_id===r.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...l];e[n]=r.data,a(e),Z(r.data)}S(!1)}}catch(e){console.error("Error creating the team:",e)}};return console.log("received teams ".concat(l)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"All Teams"}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Team Name"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"}),(0,r.jsx)(ev.Z,{children:"Info"})]})}),(0,r.jsx)(eb.Z,{children:l&&l.length>0?l.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,r.jsx)(e_.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,r.jsxs)(el.Z,{children:["TPM:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].keys&&E[e.team_id].keys.length," Keys"]}),(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].team_info&&E[e.team_id].team_info.members_with_roles&&E[e.team_id].team_info.members_with_roles.length," Members"]})]}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>O(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>L(e.team_id),icon:ex.Z,size:"sm"})]})]},e.team_id)):null})]}),C&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:D,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{I(!1),T(null)},children:"Cancel"})]})]})]})})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>_(!0),children:"+ Create New Team"}),(0,r.jsx)(en.Z,{title:"Create Team",visible:b,width:800,footer:null,onOk:()=>{_(!1),c.resetFields()},onCancel:()=>{_(!1),c.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:B,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"Team Members"}),(0,r.jsx)(u,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>{Z(e)},children:e.team_alias},l))}):(0,r.jsxs)(u,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:f?f.members_with_roles.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.role})]},l)):null})]})}),f&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,team:s,onSubmit:a}=e,[n]=er.Z.useForm();return(0,r.jsx)(en.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a({...e,team_id:s.team_id}),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:M,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A&&A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:j,onCancel:()=>{g(!1),Z(null)},team:f,onSubmit:M})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto mb-5",onClick:()=>S(!0),children:"+ Add member"}),(0,r.jsx)(en.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{S(!1),d.resetFields()},onCancel:()=>{S(!1),d.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},e9=t(18190),le=e=>{let l,{searchParams:t,accessToken:s,showSSOBanner:a}=e,[o]=er.Z.useForm(),[i]=er.Z.useForm(),{Title:c,Paragraph:d}=eR.default,[m,u]=(0,n.useState)(""),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)(!1),[f,y]=(0,n.useState)(!1),[Z,w]=(0,n.useState)(!1),[b,_]=(0,n.useState)(!1),[k,v]=(0,n.useState)(!1);try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let S=()=>{v(!1)},A=["proxy_admin","proxy_admin_viewer"];(0,n.useEffect)(()=>{(async()=>{if(null!=s){let e=[],l=await M(s,"proxy_admin_viewer");l.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(l));let t=await M(s,"proxy_admin");t.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(t)),console.log("combinedList: ".concat(e)),p(e)}})()},[s]);let N=()=>{w(!1),i.resetFields()},C=()=>{w(!1),i.resetFields()},I=e=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]}),P=(e,l,t)=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,r.jsx)(eA.Z,{value:l,children:A.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:t,valuePropName:"user_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t,disabled:!0})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Update role"})})]}),T=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,null);console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),h.ZP.success("Refresh tab to see updated user role"),w(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),g(!1)}}catch(e){console.error("Error creating the key:",e)}},F=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call"),e.user_email,e.user_id;let l=await B(s,e,"proxy_admin");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),y(!1)}}catch(e){console.error("Error creating the key:",e)}},O=async e=>{null!=s&&G(s,{environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}})};return console.log("admins: ".concat(null==x?void 0:x.length)),(0,r.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,r.jsx)(c,{level:4,children:"Admin Access "}),(0,r.jsxs)(d,{children:[a&&(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc. ",(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:x?x.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.user_role}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>w(!0)}),(0,r.jsx)(en.Z,{title:"Update role",visible:Z,width:800,footer:null,onOk:N,onCancel:C,children:P(T,e.user_role,e.user_id)})]})]},l)):null})]})})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("div",{className:"flex justify-start",children:[(0,r.jsx)(H.Z,{className:"mr-4 mb-5",onClick:()=>y(!0),children:"+ Add admin"}),(0,r.jsx)(en.Z,{title:"Add admin",visible:f,width:800,footer:null,onOk:()=>{y(!1),i.resetFields()},onCancel:()=>{y(!1),i.resetFields()},children:I(F)}),(0,r.jsx)(H.Z,{className:"mb-5",onClick:()=>g(!0),children:"+ Add viewer"}),(0,r.jsx)(en.Z,{title:"Add viewer",visible:j,width:800,footer:null,onOk:()=>{g(!1),i.resetFields()},onCancel:()=>{g(!1),i.resetFields()},children:I(E)})]})})]}),(0,r.jsxs)(Y.Z,{children:[(0,r.jsx)(c,{level:4,children:"Add SSO"}),(0,r.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,r.jsx)(H.Z,{onClick:()=>_(!0),children:"Add SSO"}),(0,r.jsx)(en.Z,{title:"Add SSO",visible:b,width:800,footer:null,onOk:()=>{_(!1),o.resetFields()},onCancel:()=>{_(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{F(e),O(e),_(!1),v(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,r.jsx)(eo.Z.Password,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,r.jsx)(eo.Z.Password,{})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,r.jsxs)(en.Z,{title:"SSO Setup Instructions",visible:k,width:800,footer:null,onOk:S,onCancel:()=>{v(!1)},children:[(0,r.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{onClick:S,children:"Done"})})]})]}),(0,r.jsxs)(e9.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access ",(0,r.jsxs)("a",{href:l,target:"_blank",children:[(0,r.jsx)("b",{children:l})," "]})]})]})]})},ll=t(12224),lt=e=>{let{accessToken:l,userRole:t,userID:s}=e,[a,o]=(0,n.useState)([]),[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)(!1),[u]=er.Z.useForm(),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)(""),[Z,w]=(0,n.useState)({}),[b,_]=(0,n.useState)([]),k=e=>{b.includes(e)?_(b.filter(l=>l!==e)):_([...b,e])},v={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),o(e.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 t=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",t),_(e.active_alerts),y(t),w(e.alerts_to_webhook)}c(l)})},[l,t,s]);let S=e=>b&&b.includes(e),A=e=>{if(!l)return;let t=Object.fromEntries(Object.entries(e.variables).map(e=>{var l;let[t,s]=e;return[t,(null===(l=document.querySelector('input[name="'.concat(t,'"]')))||void 0===l?void 0:l.value)||s]}));console.log("updatedVariables",t),console.log("updateAlertTypes",j);try{G(l,{environment_variables:t})}catch(e){h.ZP.error("Failed to update callback: "+e,20)}h.ZP.success("Callback updated successfully")},N=()=>{l&&u.validateFields().then(e=>{if(console.log("Form values:",e),"langfuse"===e.callback){G(l,{environment_variables:{LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey},litellm_settings:{success_callback:[e.callback]}});let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:null,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey}};o(a?[...a,t]:[t])}else if("slack"===e.callback){console.log("values.slackWebhookUrl: ".concat(e.slackWebhookUrl)),G(l,{general_settings:{alerting:["slack"],alerting_threshold:300},environment_variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl}}),console.log("values.callback: ".concat(e.callback));let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:null,LANGFUSE_SECRET_KEY:null}};o(a?[...a,t]:[t])}m(!1),u.resetFields(),p(null)})};return l?(console.log("callbacks: ".concat(a)),(0,r.jsxs)("div",{className:"w-full mx-4",children:[(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Logging Callbacks"}),(0,r.jsx)(eU.Z,{value:"2",children:"Alerting"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Callback"}),(0,r.jsx)(ev.Z,{children:"Callback Env Vars"})]})}),(0,r.jsx)(eb.Z,{children:a.map((e,t)=>{var s;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ep.Z,{color:"emerald",children:e.name})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)("ul",{children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).filter(e=>{let[l,t]=e;return null!==t}).map(e=>{let[l,t]=e;return(0,r.jsxs)("li",{children:[(0,r.jsx)(el.Z,{className:"mt-2",children:l}),"LANGFUSE_HOST"===l?(0,r.jsx)("p",{children:"default value=https://cloud.langfuse.com"}):(0,r.jsx)("div",{}),(0,r.jsx)($.Z,{name:l,defaultValue:t,type:"password"})]},l)})}),(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>A(e),children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,e.name),className:"mx-2",children:"Test Callback"})]})]},t)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{console.log("Add callback clicked"),m(!0)},children:"Add Callback"})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(el.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from ",(0,r.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{children:"Slack Webhook URL"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(v).map((e,l)=>{let[t,s]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ll.Z,{id:"switch",name:"switch",checked:S(t),onChange:()=>k(t)})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:s})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:t,type:"password",defaultValue:Z&&Z[t]?Z[t]:f})})]},l)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(v).forEach(l=>{let[t,s]=l,a=document.querySelector('input[name="'.concat(t,'"]'));console.log("key",t),console.log("webhookInput",a);let r=(null==a?void 0:a.value)||"";console.log("newWebhookValue",r),e[t]=r}),console.log("updatedAlertToWebhooks",e);let t={general_settings:{alert_to_webhook_url:e,alert_types:b}};console.log("payload",t);try{G(l,t)}catch(e){h.ZP.error("Failed to update alerts: "+e,20)}h.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,"slack"),className:"mx-2",children:"Test Alerts"})]})})]})]})}),(0,r.jsx)(en.Z,{title:"Add Callback",visible:d,onOk:N,width:800,onCancel:()=>{m(!1),u.resetFields(),p(null)},footer:null,children:(0,r.jsxs)(er.Z,{form:u,layout:"vertical",onFinish:N,children:[(0,r.jsx)(er.Z.Item,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,r.jsx)(ea.default,{onChange:e=>{p(e)},children:(0,r.jsx)(ea.default.Option,{value:"langfuse",children:"langfuse"})})}),"langfuse"===x&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PUBLIC_KEY",name:"langfusePublicKey",rules:[{required:!0,message:"Please enter the public key"}],children:(0,r.jsx)($.Z,{type:"password"})}),(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PRIVATE_KEY",name:"langfusePrivateKey",rules:[{required:!0,message:"Please enter the private key"}],children:(0,r.jsx)($.Z,{type:"password"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:ls}=ea.default;var la=e=>{let{models:l,accessToken:t,routerSettings:s,setRouterSettings:a}=e,[o]=er.Z.useForm(),[i,c]=(0,n.useState)(!1),[d,m]=(0,n.useState)("");return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>c(!0),children:"+ Add Fallbacks"}),(0,r.jsx)(en.Z,{title:"Add Fallbacks",visible:i,width:800,footer:null,onOk:()=>{c(!1),o.resetFields()},onCancel:()=>{c(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{console.log(e);let{model_name:l,models:r}=e,n=[...s.fallbacks||[],{[l]:r}],i={...s,fallbacks:n};console.log(i);try{G(t,{router_settings:i}),a(i)}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully"),c(!1),o.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,r.jsx)(eA.Z,{defaultValue:d,children:l&&l.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsx)(eK.Z,{value:l,children:l&&l.filter(e=>e!=d).map(e=>(0,r.jsx)(eV.Z,{value:e,children:e},e))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},lr=t(12968);async function ln(e,l){console.log("isLocal:",!1);let t=window.location.origin,s=new lr.ZP.OpenAI({apiKey:l,baseURL:t,dangerouslyAllowBrowser:!0});try{let l=await s.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});h.ZP.success((0,r.jsxs)("span",{children:["Test model=",(0,r.jsx)("strong",{children:e}),", received model=",(0,r.jsx)("strong",{children:l.model}),". See ",(0,r.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lo=e=>{let{accessToken:l,userRole:t,userID:s,modelData:a}=e,[o,i]=(0,n.useState)({}),[c,d]=(0,n.useState)(!1),[m]=er.Z.useForm(),[u,x]=(0,n.useState)(null),p={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"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),i(e.router_settings)})},[l,t,s]);let j=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(o.fallbacks)),o.fallbacks.map(l=>(e in l&&delete l[e],l));try{await G(l,{router_settings:o}),i({...o}),h.ZP.success("Router settings updated successfully")}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}}},g=e=>{if(!l)return;console.log("router_settings",e);let t=Object.fromEntries(Object.entries(e).map(e=>{let[l,t]=e;if("routing_strategy_args"!==l){var s;return[l,(null===(s=document.querySelector('input[name="'.concat(l,'"]')))||void 0===s?void 0:s.value)||t]}return null}).filter(e=>null!==e));console.log("updatedVariables",t);try{G(l,{router_settings:t})}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully")};return l?(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"General Settings"}),(0,r.jsx)(eU.Z,{value:"2",children:"Fallbacks"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,r.jsx)(et.Z,{children:"Router Settings"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Setting"}),(0,r.jsx)(ev.Z,{children:"Value"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(o).filter(e=>{let[l,t]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l}).map(e=>{let[l,t]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(el.Z,{children:l}),(0,r.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:p[l]})]}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:l,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t.toString()})})]},l)})})]})}),(0,r.jsx)(J.Z,{children:(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>g(o),children:"Save Changes"})})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Model Name"}),(0,r.jsx)(ev.Z,{children:"Fallbacks"})]})}),(0,r.jsx)(eb.Z,{children:o.fallbacks&&o.fallbacks.map((e,t)=>Object.entries(e).map(e=>{let[s,a]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:s}),(0,r.jsx)(e_.Z,{children:Array.isArray(a)?a.join(", "):a}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(H.Z,{onClick:()=>ln(s,l),children:"Test Fallback"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(eZ.Z,{icon:ex.Z,size:"sm",onClick:()=>j(s)})})]},t.toString()+s)}))})]}),(0,r.jsx)(la,{models:(null==a?void 0:a.data)?a.data.map(e=>e.model_name):[],accessToken:l,routerSettings:o,setRouterSettings:i})]})]})]})}):null},li=t(67951),lc=e=>{let{}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsxs)("div",{className:"mb-5",children:[(0,r.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,r.jsx)(el.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,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{children:[(0,r.jsx)(eU.Z,{children:"OpenAI Python SDK"}),(0,r.jsx)(eU.Z,{children:"LlamaIndex"}),(0,r.jsx)(eU.Z,{children:"Langchain Py"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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 = "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 ld(e,l,t,s){console.log("isLocal:",!1);let a=window.location.origin,r=new lr.ZP.OpenAI({apiKey:s,baseURL:a,dangerouslyAllowBrowser:!0});try{for await(let s of(await r.chat.completions.create({model:t,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&l(s.choices[0].delta.content)}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lm=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)(""),[m,h]=(0,n.useState)([]),[u,x]=(0,n.useState)(void 0),[p,j]=(0,n.useState)([]);(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{let e=await v(l,a,s);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}));console.log(l),j(l),x(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,a,s]);let g=(e,l)=>{h(t=>{let s=t[t.length-1];return s&&s.role===e?[...t.slice(0,t.length-1),{role:e,content:s.content+l}]:[...t,{role:e,content:l}]})},f=async()=>{if(""!==c.trim()&&o&&t&&s&&a){h(e=>[...e,{role:"user",content:c}]);try{u&&await ld(c,e=>g("assistant",e),u,o)}catch(e){console.error("Error fetching model response",e),g("assistant","Error fetching model response")}d("")}};if(s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,r.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsx)(ez.Z,{children:(0,r.jsx)(eU.Z,{children:"Chat"})}),(0,r.jsx)(eq.Z,{children:(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)("div",{className:"sm:max-w-2xl",children:(0,r.jsxs)(Y.Z,{numItems:2,children:[(0,r.jsxs)(J.Z,{children:[(0,r.jsx)(el.Z,{children:"API Key"}),(0,r.jsx)($.Z,{placeholder:"Type API Key here",type:"password",onValueChange:i,value:o})]}),(0,r.jsxs)(J.Z,{className:"mx-2",children:[(0,r.jsx)(el.Z,{children:"Select Model:"}),(0,r.jsx)(ea.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),x(e)},options:p,style:{width:"200px"}})]})]})}),(0,r.jsxs)(ew.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,r.jsx)(ek.Z,{children:(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{})})}),(0,r.jsx)(eb.Z,{children:m.map((e,l)=>(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,r.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)($.Z,{type:"text",value:c,onChange:e=>d(e.target.value),placeholder:"Type your message..."}),(0,r.jsx)(H.Z,{onClick:f,className:"ml-2",children:"Send"})]})})]})})]})})})})},lh=t(33509),lu=t(95781);let{Sider:lx}=lh.default;var lp=e=>{let{setPage:l,userRole:t,defaultSelectedKey:s}=e;return"Admin Viewer"==t?(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,r.jsx)(lx,{width:120,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["4"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:"API Keys"},"4"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:"Models"},"2"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:"Chat UI"},"3"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1")]})})}):(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,r.jsx)(lx,{width:145,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["1"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:(0,r.jsx)(el.Z,{children:"API Keys"})},"1"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:(0,r.jsx)(el.Z,{children:"Test Key"})},"3"),"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:(0,r.jsx)(el.Z,{children:"Models"})},"2"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:(0,r.jsx)(el.Z,{children:"Usage"})},"4"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("teams"),children:(0,r.jsx)(el.Z,{children:"Teams"})},"6"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("users"),children:(0,r.jsx)(el.Z,{children:"Users"})},"5"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("settings"),children:(0,r.jsx)(el.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("general-settings"),children:(0,r.jsx)(el.Z,{children:"Router Settings"})},"9"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("admin-panel"),children:(0,r.jsx)(el.Z,{children:"Admin"})},"7"):null,(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api_ref"),children:(0,r.jsx)(el.Z,{children:"API Reference"})},"11")]})})})},lj=t(67989),lg=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)([]),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)([]),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),[b,v]=(0,n.useState)([]),[S,T]=(0,n.useState)([]),[O,M]=(0,n.useState)([]),[R,L]=(0,n.useState)([]),[U,D]=(0,n.useState)(null),z=new Date(o.getFullYear(),o.getMonth(),1),B=new Date(o.getFullYear(),o.getMonth()+1,0),q=V(z),K=V(B);function V(e){let l=e.getFullYear(),t=e.getMonth()+1,s=e.getDate();return"".concat(l,"-").concat(t<10?"0"+t:t,"-").concat(s<10?"0"+s:s)}console.log("Start date is ".concat(q)),console.log("End date is ".concat(K)),(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{if(console.log("user role: ".concat(s)),"Admin"==s||"Admin Viewer"==s){let e=await I(l);c(e);let t=(await P(l)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,10),spend:e.total_spend}));m(t);let r=(await E(l)).map(e=>({key:e.model,spend:e.total_spend}));u(r);let n=await A(l);console.log("teamSpend",n),g(n.daily_spend),w(n.teams);let o=n.total_spend_per_team;o=o.map(e=>(e.name=e.team_id||"",e.value=e.total_spend||0,e)),v(o);let i=await N(l);y(i.top_10_tags);let d=(await _(l,a,s)).data;console.log("model groups in model dashboard",d);let h=[];for(let e=0;e{if(console.log("result from spend logs call",e),"daily_spend"in e){let l=e.daily_spend;console.log("daily spend",l),c(l);let t=e.top_api_keys;m(t)}else{let t=(await F(l,function(e){let l=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[t,s]=e;"spend"!==t&&"startTime"!==t&&"models"!==t&&"users"!==t&&l.push({key:t,spend:s})})}),l.sort((e,l)=>Number(l.spend)-Number(e.spend));let t=l.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(t[0]))),t}(e))).info.map(e=>({key:(e.key_name||e.key_alias).substring(0,10),spend:e.spend}));m(t),p(function(e){let l={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[t,s]=e;""!==t&&null!=t&&"None"!=t&&(l[t]||(l[t]=0),l[t]+=s)})});let t=Object.entries(l).map(e=>{let[l,t]=e;return{user_id:l,spend:t}});t.sort((e,l)=>l.spend-e.spend);let s=t.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)}});let e=await k(l,a,s,null);console.log("Model metrics response:",e);let r=[...e].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(e),M(r)}catch(e){console.error("There was an error fetching the data",e)}})()},[l,t,s,a,q,K]);let G=async e=>{if(console.log("Updating model metrics for group:",e),l&&a&&s){D(e);try{let t=await k(l,a,s,e);console.log("Model metrics response:",t);let r=[...t].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(t),M(r)}catch(e){console.error("Failed to fetch model metrics",e)}}};return(0,r.jsxs)("div",{style:{width:"100%"},className:"p-8",children:[(0,r.jsx)(eE,{userID:a,userRole:s,accessToken:l,userSpend:null,selectedTeam:null}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{className:"mt-2",children:[(0,r.jsx)(eU.Z,{children:"All Up"}),(0,r.jsx)(eU.Z,{children:"Team Based Usage"}),(0,r.jsx)(eU.Z,{children:"Tag Based Usage"}),(0,r.jsx)(eU.Z,{children:"Model Based Usage"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Monthly Spend"}),(0,r.jsx)(ej.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top API Keys"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Users"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Models"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:h,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsxs)(J.Z,{numColSpan:2,children:[(0,r.jsxs)(eg.Z,{className:"mb-2",children:[(0,r.jsx)(et.Z,{children:"Total Spend Per Team"}),(0,r.jsx)(lj.Z,{data:b})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Daily Spend Per Team"}),(0,r.jsx)(ej.Z,{className:"h-72",data:j,showLegend:!0,index:"date",categories:Z,yAxisWidth:80,colors:["blue","green","yellow","red","purple"],stack:!0})]})]}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Spend Per Tag - Last 30 Days"}),(0,r.jsxs)(el.Z,{children:["Get Started Tracking cost per tag ",(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/enterprise#tracking-spend-for-custom-tags",target:"_blank",children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Tag"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Requests"})]})}),(0,r.jsx)(eb.Z,{children:f.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.name}),(0,r.jsx)(e_.Z,{children:e.value}),(0,r.jsx)(e_.Z,{children:e.log_count})]},e.name))})]})]})}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)(et.Z,{children:"Filter By Model Group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080"},children:"View how requests were load balanced within a model group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080",fontStyle:"italic"},children:"(Beta feature) only supported for Azure Model Groups"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2",defaultValue:"all",children:[(0,r.jsx)(eN.Z,{value:"all",onClick:()=>G(null),children:"All Model Groups"}),R.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>G(e),children:e},l))]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Number Requests per Model"}),(0,r.jsx)(ej.Z,{data:S,className:"h-[50vh]",index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,r.jsxs)(eg.Z,{className:"mt-4",children:[(0,r.jsx)(et.Z,{children:"Latency Per Model"}),(0,r.jsx)(ej.Z,{data:O,className:"h-[50vh]",index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})]})]})]})},lf=()=>{let{Title:e,Paragraph:l}=eR.default,[t,s]=(0,n.useState)(""),[a,i]=(0,n.useState)(null),[c,d]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(!0),j=(0,o.useSearchParams)(),[g,f]=(0,n.useState)({data:[]}),y=j.get("userID"),Z=j.get("token"),[w,b]=(0,n.useState)("api-keys"),[_,k]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(Z){let e=(0,eM.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(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"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),s(l),"Admin Viewer"==l&&b("usage")}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[Z]),(0,r.jsx)(n.Suspense,{fallback:(0,r.jsx)("div",{children:"Loading..."}),children:(0,r.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,r.jsx)(m,{userID:y,userRole:t,userEmail:a,showSSOBanner:x}),(0,r.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,r.jsx)("div",{className:"mt-8",children:(0,r.jsx)(lp,{setPage:b,userRole:t,defaultSelectedKey:null})}),"api-keys"==w?(0,r.jsx)(eL,{userID:y,userRole:t,teams:c,keys:h,setUserRole:s,userEmail:a,setUserEmail:i,setTeams:d,setKeys:u}):"models"==w?(0,r.jsx)(e8,{userID:y,userRole:t,token:Z,accessToken:_,modelData:g,setModelData:f}):"llm-playground"==w?(0,r.jsx)(lm,{userID:y,userRole:t,token:Z,accessToken:_}):"users"==w?(0,r.jsx)(e6,{userID:y,userRole:t,token:Z,keys:h,teams:c,accessToken:_,setKeys:u}):"teams"==w?(0,r.jsx)(e7,{teams:c,setTeams:d,searchParams:j,accessToken:_,userID:y,userRole:t}):"admin-panel"==w?(0,r.jsx)(le,{setTeams:d,searchParams:j,accessToken:_,showSSOBanner:x}):"api_ref"==w?(0,r.jsx)(lc,{}):"settings"==w?(0,r.jsx)(lt,{userID:y,userRole:t,accessToken:_}):"general-settings"==w?(0,r.jsx)(lo,{userID:y,userRole:t,accessToken:_,modelData:g}):(0,r.jsx)(lg,{userID:y,userRole:t,token:Z,accessToken:_})]})]})})}}},function(e){e.O(0,[447,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/PtTtxXIYvdjQsvRgdITlk/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/index.html b/litellm/proxy/_experimental/out/index.html index cb197f11e23..0dbd09152f7 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 267d8e5baf0..70ccce5418b 100644 --- a/litellm/proxy/_experimental/out/index.txt +++ b/litellm/proxy/_experimental/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[27125,["447","static/chunks/447-9f8d32190ff7d16d.js","931","static/chunks/app/page-781ca5f151d78d1d.js"],""] +3:I[27125,["447","static/chunks/447-9f8d32190ff7d16d.js","931","static/chunks/app/page-508c39694bd40fe9.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["PtTtxXIYvdjQsvRgdITlk",[[["",{"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_c23dc8","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/5e699db73bf6f8c2.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["kbGdRQFfI6W3bEwfzmJDI",[[["",{"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_c23dc8","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/5e699db73bf6f8c2.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 310a90e222d..6d3d33c80bf 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-508c39694bd40fe9.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-508c39694bd40fe9.js new file mode 100644 index 00000000000..290d55662cb --- /dev/null +++ b/ui/litellm-dashboard/out/_next/static/chunks/app/page-508c39694bd40fe9.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,t){Promise.resolve().then(t.bind(t,27125))},27125:function(e,l,t){"use strict";t.r(l),t.d(l,{default:function(){return lf}});var s,a,r=t(3827),n=t(64090),o=t(47907),i=t(8792),c=t(40491),d=t(65270),m=e=>{let{userID:l,userRole:t,userEmail:s,showSSOBanner:a}=e;console.log("User ID:",l),console.log("userEmail:",s),console.log("showSSOBanner:",a);let n=[{key:"1",label:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["Role: ",t]}),(0,r.jsxs)("p",{children:["ID: ",l]})]})}];return(0,r.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,r.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,r.jsx)("div",{className:"flex flex-col items-center",children:(0,r.jsx)(i.default,{href:"/",children:(0,r.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,r.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,r.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,r.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,r.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Request hosted proxy"})}):null,(0,r.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,r.jsx)(c.Z,{menu:{items:n},children:(0,r.jsx)(d.Z,{children:s})})})]})]})},h=t(80588);let u=async()=>{try{let e=await fetch("https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"),l=await e.json();return console.log("received data: ".concat(l)),l}catch(e){throw console.error("Failed to get model cost map:",e),e}},x=async(e,l)=>{try{let t=await fetch("/model/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model created successfully. Wait 60s and refresh on 'All Models' page"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,l)=>{console.log("model_id in model delete call: ".concat(l));try{let t=await fetch("/model/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model deleted successfully. Restart server to see this."),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,l)=>{try{console.log("in keyDeleteCall:",l);let t=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,l)=>{try{console.log("in teamDeleteCall:",l);let t=await fetch("/team/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete team: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to delete key:",e),e}},Z=async function(e,l,t){let s=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,r=arguments.length>5?arguments[5]:void 0;try{let n="/user/info";"App Owner"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),"App User"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),console.log("in userInfoCall viewAll=",s),s&&r&&null!=a&&void 0!=a&&(n="".concat(n,"?view_all=true&page=").concat(a,"&page_size=").concat(r));let o=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let i=await o.json();return console.log("API Response:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},w=async(e,l)=>{try{let t="/team/info";l&&(t="".concat(t,"?team_id=").concat(l)),console.log("in teamInfoCall");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let l=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,l,t)=>{try{let l=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log("modelInfoCall:",t),t}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,l,t,s)=>{try{let l="/model/metrics";s&&(l="".concat(l,"?_selected_model_group=").concat(s));let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,l,t)=>{try{let l=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,l)=>{try{let t="/global/spend/logs";console.log("in keySpendLogsCall:",t);let s=await fetch("".concat(t,"?api_key=").concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let l="/global/spend/teams";console.log("in teamSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let l="/global/spend/tags";console.log("in tagsSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},C=async(e,l,t,s,a,r)=>{try{console.log("user role in spend logs call: ".concat(t));let l="/spend/logs";l="App Owner"==t?"".concat(l,"?user_id=").concat(s,"&start_date=").concat(a,"&end_date=").concat(r):"".concat(l,"?start_date=").concat(a,"&end_date=").concat(r);let n=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},I=async e=>{try{let l=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{let l=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,l)=>{try{l&&JSON.stringify({api_key:l});let t={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};l&&(t.body=JSON.stringify({api_key:l}));let s=await fetch("/global/spend/end_users",t);if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},E=async e=>{try{let l=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},F=async(e,l)=>{try{let t=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},O=async e=>{try{let l="/user/get_requests";console.log("in userGetRequesedtModelsCall:",l);let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to get requested models:",e),e}},M=async(e,l)=>{try{let t="/user/get_users?role=".concat(l);console.log("in userGetAllUsersCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},R=async(e,l)=>{try{console.log("Form Values in teamCreateCall:",l);let t=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,l)=>{try{console.log("Form Values in keyUpdateCall:",l);let t=await fetch("/key/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update key Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,l)=>{try{console.log("Form Values in teamUpateCall:",l);let t=await fetch("/team/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update team: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update Team Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async(e,l)=>{try{console.log("Form Values in modelUpateCall:",l);let t=await fetch("/model/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update model: "+e,20),console.error("Error update from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update model Response:",s),s}catch(e){throw console.error("Failed to update model:",e),e}},z=async(e,l,t)=>{try{console.log("Form Values in teamMemberAddCall:",t);let s=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:l,member:t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,l,t)=>{try{console.log("Form Values in userUpdateUserCall:",l);let s={...l};null!==t&&(s.user_role=t),s=JSON.stringify(s);let a=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:s});if(!a.ok){let e=await a.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await a.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,l)=>{try{let t=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,l)=>{try{let t="/health/services?service=".concat(l);console.log("Checking Slack Budget Alerts service health");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed ".concat(l," service health check ")+e),Error(e)}let a=await s.json();return h.ZP.success("Test request to ".concat(l," made - check logs/alerts on ").concat(l," to verify")),a}catch(e){throw console.error("Failed to perform health check:",e),e}},V=async(e,l,t)=>{try{let l=await fetch("/get/config/callbacks",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},G=async(e,l)=>{try{let t=await fetch("/config/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},W=async e=>{try{let l=await fetch("/health",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to call /health:",e),e}};var J=t(10384),Y=t(46453),H=t(2179),$=t(52273),X=t(26780),Q=t(15595),ee=t(6698),el=t(71801),et=t(42440),es=t(42308),ea=t(50670),er=t(81583),en=t(99129),eo=t(44839),ei=t(88707),ec=t(1861);let{Option:ed}=ea.default;var em=e=>{let{userID:l,team:t,userRole:s,accessToken:a,data:o,setData:i}=e,[c]=er.Z.useForm(),[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(null),[p,g]=(0,n.useState)(null),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),b=()=>{m(!1),c.resetFields()},_=()=>{m(!1),x(null),c.resetFields()};(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==a){let e=(await v(a,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),y(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,s]);let k=async e=>{try{h.ZP.info("Making API Call"),m(!0);let t=await j(a,l,e);console.log("key create Response:",t),i(e=>e?[...e,t]:[t]),x(t.key),g(t.soft_budget),h.ZP.success("API Key Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e)}};return(0,n.useEffect)(()=>{w(t&&t.models.length>0?t.models.includes("all-proxy-models")?f:t.models:f)},[t,f]),(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,r.jsx)(en.Z,{title:"Create Key",visible:d,width:800,footer:null,onOk:b,onCancel:_,children:(0,r.jsxs)(er.Z,{form:c,onFinish:k,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:t?t.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t?t.team_alias:"",disabled:!0})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&c.setFieldsValue({models:["all-team-models"]})},children:[(0,r.jsx)(ed,{value:"all-team-models",children:"All Team Models"},"all-team-models"),Z.map(e=>(0,r.jsx)(ed,{value:e,children:e},e))]})}),(0,r.jsxs)(X.Z,{className:"mt-20 mb-8",children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)("b",{children:"Optional Settings"})}),(0,r.jsxs)(Q.Z,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==t?void 0:t.max_budget)!==null&&(null==t?void 0:t.max_budget)!==void 0?null==t?void 0:t.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.max_budget&&l>t.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(t.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==t?void 0:t.budget_duration)!==null&&(null==t?void 0:t.budget_duration)!==void 0?null==t?void 0:t.budget_duration:"None"),children:(0,r.jsxs)(ea.default,{defaultValue:null,placeholder:"n/a",children:[(0,r.jsx)(ea.default.Option,{value:"24h",children:"daily"}),(0,r.jsx)(ea.default.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==t?void 0:t.tpm_limit)!==null&&(null==t?void 0:t.tpm_limit)!==void 0?null==t?void 0:t.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.tpm_limit&&l>t.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(t.tpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==t?void 0:t.rpm_limit)!==null&&(null==t?void 0:t.rpm_limit)!==void 0?null==t?void 0:t.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.rpm_limit&&l>t.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(t.rpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Key"})})]})}),u&&(0,r.jsx)(en.Z,{visible:d,onOk:b,onCancel:_,footer:null,children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Save your Key"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,r.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,r.jsx)(J.Z,{numColSpan:1,children:null!=u?(0,r.jsxs)("div",{children:[(0,r.jsx)(el.Z,{className:"mt-3",children:"API Key:"}),(0,r.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,r.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:u})}),(0,r.jsx)(es.CopyToClipboard,{text:u,onCopy:()=>{h.ZP.success("API Key copied to clipboard")},children:(0,r.jsx)(H.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,r.jsx)(el.Z,{children:"Key being created, this might take 30s"})})]})})]})},eh=t(9454),eu=t(98941),ex=t(33393),ep=t(5),ej=t(9853),eg=t(13810),ef=t(39290),ey=t(66952),eZ=t(61244),ew=t(10827),eb=t(3851),e_=t(2044),ek=t(64167),ev=t(74480),eS=t(7178),eA=t(95093),eN=t(27166);let{Option:eC}=ea.default;var eI=e=>{let{userID:l,userRole:t,accessToken:s,selectedTeam:a,data:o,setData:i,teams:c}=e,[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(!1),[p,j]=(0,n.useState)(null),[g,y]=n.useState(null),[Z,w]=(0,n.useState)(null),[b,_]=(0,n.useState)(null),[k,A]=(0,n.useState)(""),[N,C]=(0,n.useState)(!1),[I,P]=(0,n.useState)(null),[T,E]=(0,n.useState)([]),F=new Set,[O,M]=(0,n.useState)(F);(0,n.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==s&&null!==t){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),E(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[s,l,t]),(0,n.useEffect)(()=>{if(c){let e=new Set;c.forEach((l,t)=>{let s=l.team_id;e.add(s)}),M(e)}},[c]);let R=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),P(e),C(!0)},U=async e=>{if(null==s)return;let l=e.token;e.key=l,console.log("handleEditSubmit:",e);let t=await L(s,e);console.log("handleEditSubmit: newKeyValues",t),o&&i(o.map(e=>e.token===l?t:e)),h.ZP.success("Key updated successfully"),C(!1),P(null)},D=async e=>{try{if(null==s||null==e)return;console.log("accessToken: ".concat(s,"; token: ").concat(e.token));let l=await S(s,e.token);console.log("Response:",l),_(l);try{let e=await q(s,l);console.log("Response2:",e);let t=[...l,...e.response];_(t),A(e.predicted_spend),console.log("Combined Data:",t)}catch(e){console.error("There was an error fetching the predicted data",e)}}catch(e){console.error("There was an error fetching the data",e)}};(0,n.useEffect)(()=>{D(Z)},[Z]);let z=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=o&&(j(e.token),localStorage.removeItem("userData"+l),x(!0))},B=async()=>{if(null!=p&&null!=o){try{await f(s,p);let e=o.filter(e=>e.token!==p);i(e)}catch(e){console.error("Error deleting the key:",e)}x(!1),j(null)}};if(null!=o)return console.log("RERENDER TRIGGERED"),(0,r.jsxs)("div",{children:[(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,r.jsxs)(ew.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Key Alias"}),(0,r.jsx)(ev.Z,{children:"Secret Key"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"})]})}),(0,r.jsx)(eb.Z,{children:o.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(a){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(O.has(e.team_id),", selectedTeam id: ").concat(a.team_id)),(null!=a.team_id||null===e.team_id||O.has(e.team_id))&&e.team_id!=a.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,r.jsx)(el.Z,{children:e.key_alias}):(0,r.jsx)(el.Z,{children:"Not Set"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.key_name})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,r.jsx)(e_.Z,{children:null!=e.max_budget?(0,r.jsx)(el.Z,{children:e.max_budget}):(0,r.jsx)(el.Z,{children:"Unlimited"})}),(0,r.jsx)(e_.Z,{children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(r.Fragment,{children:a&&a.models&&a.models.length>0?a.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(el.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{onClick:()=>{w(e),y(e.id)},icon:eh.Z,size:"sm"}),(0,r.jsx)(ef.Z,{open:null!==g,onClose:()=>{y(null),w(null)},children:(0,r.jsx)(ey.Z,{children:Z&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",children:[(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(Z.spend).toFixed(4)}catch(e){return Z.spend}})()})})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.max_budget?(0,r.jsx)(r.Fragment,{children:Z.max_budget}):(0,r.jsx)(r.Fragment,{children:"Unlimited"})})})]},e.name),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.expires?(0,r.jsx)(r.Fragment,{children:new Date(Z.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,r.jsx)(r.Fragment,{children:"Never"})})})]},e.name)]}),(0,r.jsx)(eg.Z,{className:"mt-6 mb-6",children:b&&(0,r.jsx)(ej.Z,{className:"mt-6",data:b,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})}),(0,r.jsx)(et.Z,{children:"Metadata"}),(0,r.jsx)(el.Z,{children:JSON.stringify(Z.metadata)}),(0,r.jsx)(H.Z,{variant:"light",className:"mx-auto flex items-center",onClick:()=>{y(null),w(null)},children:"Close"})]})})}),(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>R(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>z(e),icon:ex.Z,size:"sm"})]})]},e.token)})})]}),u&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:B,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{x(!1),j(null)},children:"Cancel"})]})]})]})})]}),I&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,token:s,onSubmit:o}=e,[i]=er.Z.useForm(),[d,m]=(0,n.useState)(a),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)(!1);return(0,r.jsx)(en.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{i.validateFields().then(e=>{i.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:i,onFinish:U,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let t=l.filter(e=>!d.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!d.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(t)),t.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(t,"Team models: ").concat(d.models)):Promise.resolve()}}],children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(eC,{value:"all-team-models",children:"All Team Models"},"all-team-models"),d&&d.models?d.models.includes("all-proxy-models")?T.filter(e=>"all-proxy-models"!==e).map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):d.models.map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):T.map(e=>(0,r.jsx)(eC,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==d?void 0:d.max_budget)!==null&&(null==d?void 0:d.max_budget)!==void 0?null==d?void 0:d.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&d&&null!==d.max_budget&&l>d.max_budget)throw console.log("keyTeam.max_budget: ".concat(d.max_budget)),Error("Budget cannot exceed team max budget: $".concat(d.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"token",name:"token",hidden:!0}),(0,r.jsx)(er.Z.Item,{label:"Team",name:"team_id",help:"the team this key belongs to",children:(0,r.jsx)(eA.Z,{value:s.team_alias,children:null==c?void 0:c.map((e,l)=>(0,r.jsx)(eN.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:N,onCancel:()=>{C(!1),P(null)},token:I,onSubmit:U})]})},eP=t(76032),eT=t(35152),eE=e=>{let{userID:l,userRole:t,accessToken:s,userSpend:a,selectedTeam:o}=e;console.log("userSpend: ".concat(a));let[i,c]=(0,n.useState)(null!==a?a:0),[d,m]=(0,n.useState)(0),[h,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{let e=async()=>{if(s&&l&&t&&"Admin"===t&&null==a)try{let e=await b(s);e&&(e.spend?c(e.spend):c(0),e.max_budget?m(e.max_budget):m(0))}catch(e){console.error("Error fetching global spend data:",e)}};(async()=>{try{if(null===l||null===t)return;if(null!==s){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),u(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,s,l]),(0,n.useEffect)(()=>{null!==a&&c(a)},[a]);let x=[];o&&o.models&&(x=o.models),x&&x.includes("all-proxy-models")?(console.log("user models:",h),x=h):x&&x.includes("all-team-models")?x=o.models:x&&0===x.length&&(x=h);let p=void 0!==i?i.toFixed(4):null;return console.log("spend in view user spend: ".concat(i)),(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:["Total Spend"," "]}),(0,r.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",p]})]}),(0,r.jsx)("div",{className:"ml-auto",children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Team Models"})}),(0,r.jsx)(Q.Z,{className:"absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs",children:(0,r.jsx)(eP.Z,{children:x.map(e=>(0,r.jsx)(eT.Z,{children:(0,r.jsx)(el.Z,{children:e})},e))})})]})})]})},eF=e=>{let{userID:l,userRole:t,selectedTeam:s,accessToken:a}=e,[o,i]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==a){let e=(await v(a,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),i(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,t]);let c=[];return s&&s.models&&(c=s.models),c&&c.includes("all-proxy-models")&&(console.log("user models:",o),c=o),(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"mb-5",children:(0,r.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==s?void 0:s.team_alias})})})},eO=e=>{let l,{teams:t,setSelectedTeam:s,userRole:a}=e,o={models:[],team_id:null,team_alias:"Default Team"},[i,c]=(0,n.useState)(o);return(l="App User"===a?t:t?[...t,o]:[o],"App User"===a)?null:(0,r.jsxs)("div",{className:"mt-5 mb-5",children:[(0,r.jsx)(et.Z,{children:"Select Team"}),(0,r.jsx)(el.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,r.jsxs)(el.Z,{className:"mt-3 mb-3",children:[(0,r.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,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>s(e),children:e.team_alias},l))}):(0,r.jsxs)(el.Z,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]})},eM=t(37963),eR=t(36083);console.log("isLocal:",!1);var eL=e=>{let{userID:l,userRole:t,teams:s,keys:a,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:m,setKeys:h}=e,[u,x]=(0,n.useState)(null),p=(0,o.useSearchParams)();p.get("viewSpend"),(0,o.useRouter)();let j=p.get("token"),[g,f]=(0,n.useState)(null),[y,w]=(0,n.useState)(null),[_,k]=(0,n.useState)([]),S={models:[],team_alias:"Default Team",team_id:null},[A,N]=(0,n.useState)(s?s[0]:S);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(j){let e=(0,eM.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),i(l)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&g&&t&&!a&&!u){let e=sessionStorage.getItem("userModels"+l);e?k(JSON.parse(e)):(async()=>{try{let e=await Z(g,l,t,!1,null,null);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==t){let e=await b(g);x(e),console.log("globalSpend:",e)}else x(e.user_info);h(e.keys),m(e.teams);let s=[...e.teams];s.length>0?(console.log("response['teams']: ".concat(s)),N(s[0])):N(S),sessionStorage.setItem("userData"+l,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(e.user_info));let a=(await v(g,l,t)).data.map(e=>e.id);console.log("available_model_names:",a),k(a),console.log("userModels:",_),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,j,g,a,t]),(0,n.useEffect)(()=>{if(null!==a&&null!=A){let e=0;for(let l of a)A.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===A.team_id&&(e+=l.spend);w(e)}else if(null!==a){let e=0;for(let l of a)e+=l.spend;w(e)}},[A]),null==l||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==g)return null;if(null==t&&i("App Owner"),t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",A),console.log("teamSpend: ".concat(y)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eF,{userID:l,userRole:t,selectedTeam:A||null,accessToken:g}),(0,r.jsx)(eE,{userID:l,userRole:t,accessToken:g,userSpend:y,selectedTeam:A||null}),(0,r.jsx)(eI,{userID:l,userRole:t,accessToken:g,selectedTeam:A||null,data:a,setData:h,teams:s}),(0,r.jsx)(em,{userID:l,team:A||null,userRole:t,accessToken:g,data:a,setData:h},A?A.team_id:null),(0,r.jsx)(eO,{teams:s,setSelectedTeam:N,userRole:t})]})})})},eU=t(92836),eD=t(26734),ez=t(41608),eB=t(32126),eq=t(23682),eK=t(47047),eV=t(76628),eG=t(38302),eW=t(28683),eJ=t(1460),eY=t(78578),eH=t(63954),e$=e=>{let{modelID:l,accessToken:t}=e,[s,a]=(0,n.useState)(!1),o=async()=>{try{h.ZP.info("Making API Call"),a(!0);let e=await p(t,l);console.log("model delete Response:",e),h.ZP.success("Model ".concat(l," deleted successfully")),a(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(eZ.Z,{onClick:()=>a(!0),icon:ex.Z,size:"sm"}),(0,r.jsx)(en.Z,{open:s,onOk:o,okType:"danger",onCancel:()=>a(!1),children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Delete Model"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Model ID: ",(0,r.jsx)("b",{children:l})]})})]})})]})},eX=t(97766),eQ=t(46495);let{Title:e0,Link:e1}=eR.default;(s=a||(a={})).OpenAI="OpenAI",s.Azure="Azure",s.Anthropic="Anthropic",s.Google_AI_Studio="Gemini (Google AI Studio)",s.Bedrock="Amazon Bedrock",s.OpenAI_Compatible="OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)",s.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)";let e2={OpenAI:"openai",Azure:"azure",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai"},e4=async(e,l,t)=>{try{let s=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(s)),console.log("received type of deployments: ".concat(typeof s)),s.forEach(async t=>{console.log("litellm_model: ".concat(t));let s={},a={};s.model=t;let r="";for(let[l,t]of Object.entries(e))if(""!==t){if("model_name"==l)r+=t;else if("custom_llm_provider"==l)continue;else if("model"==l)continue;else if("base_model"===l)a[l]=t;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",t);let e={};if(t&&void 0!=t){try{e=JSON.parse(t)}catch(e){throw h.ZP.error("Failed to parse LiteLLM Extra Params: "+e,20),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,t]of Object.entries(e))s[l]=t}}else s[l]=t}let n={model_name:r,litellm_params:s,model_info:a},o=await x(l,n);console.log("response for model create call: ".concat(o.data))}),t.resetFields()}catch(e){h.ZP.error("Failed to create model: "+e,20)}};var e8=e=>{var l,t,s;let{accessToken:o,token:i,userRole:c,userID:d,modelData:m={data:[]},setModelData:x}=e,[p,j]=(0,n.useState)([]),[g]=er.Z.useForm(),[f,y]=(0,n.useState)(null),[Z,w]=(0,n.useState)(""),[b,k]=(0,n.useState)([]),v=Object.values(a).filter(e=>isNaN(Number(e))),[S,A]=(0,n.useState)("OpenAI"),[N,C]=(0,n.useState)(""),[I,P]=(0,n.useState)(!1),[T,E]=(0,n.useState)(null),[F,M]=(0,n.useState)([]),[R,L]=(0,n.useState)(null),U=e=>{E(e),P(!0)},z=async e=>{if(console.log("handleEditSubmit:",e),null==o)return;let l={},t=null;for(let[s,a]of Object.entries(e))"model_id"!==s?l[s]=a:t=a;let s={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",s),await D(o,s),h.ZP.success("Model updated successfully, restart server to see updates"),P(!1),E(null)},B=()=>{w(new Date().toLocaleString())};if((0,n.useEffect)(()=>{if(!o||!i||!c||!d)return;let e=async()=>{try{let e=await _(o,d,c);console.log("Model data response:",e.data),x(e);let l=new Set;for(let t=0;t{let e=await u();console.log("received model cost map data: ".concat(Object.keys(e))),y(e)};null==f&&l(),B()},[o,i,c,d,f,Z]),!m||!o||!i||!c||!d)return(0,r.jsx)("div",{children:"Loading..."});let q=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(f)),null!=f&&"object"==typeof f&&e in f)?f[e].litellm_provider:"openai";if(a){let e=a.split("/"),l=e[0];n=1===e.length?h(a):l}else n="openai";r&&(o=null==r?void 0:r.input_cost_per_token,i=null==r?void 0:r.output_cost_per_token,c=null==r?void 0:r.max_tokens),(null==s?void 0:s.litellm_params)&&(d=Object.fromEntries(Object.entries(null==s?void 0:s.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),m.data[e].provider=n,m.data[e].input_cost=o,m.data[e].output_cost=i,m.data[e].max_tokens=c,m.data[e].api_base=null==s?void 0:null===(t=s.litellm_params)||void 0===t?void 0:t.api_base,m.data[e].cleanedLitellmParams=d,q.push(s.model_name),console.log(m.data[e])}if(c&&"Admin Viewer"==c){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let K=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(a).find(l=>a[l]===e);if(l){let e=e2[l];console.log("mappingResult: ".concat(e));let t=[];"object"==typeof f&&Object.entries(f).forEach(l=>{let[s,a]=l;null!==a&&"object"==typeof a&&"litellm_provider"in a&&(a.litellm_provider===e||a.litellm_provider.includes(e))&&t.push(s)}),k(t),console.log("providerModels: ".concat(b))}},V=async()=>{try{h.ZP.info("Running health check..."),C("");let e=await W(o);C(e)}catch(e){console.error("Error running health check:",e),C("Error running health check")}};return console.log("selectedProvider: ".concat(S)),console.log("providerModels.length: ".concat(b.length)),(0,r.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)(eU.Z,{children:"All Models"}),(0,r.jsx)(eU.Z,{children:"Add Model"}),(0,r.jsx)(eU.Z,{children:(0,r.jsx)("pre",{children:"/health Models"})})]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[Z&&(0,r.jsxs)(el.Z,{children:["Last Refreshed: ",Z]}),(0,r.jsx)(eZ.Z,{icon:eH.Z,variant:"shadow",size:"xs",className:"self-center",onClick:B})]})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(Y.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(el.Z,{children:"Filter by Public Model Name"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:"all",onValueChange:e=>L("all"===e?"all":e),children:[(0,r.jsx)(eN.Z,{value:"all",children:"All Models"}),F.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>L(e),children:e},l))]})]}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Public Model Name "}),(0,r.jsx)(ev.Z,{children:"Provider"}),"Admin"===c&&(0,r.jsx)(ev.Z,{children:"API Base"}),(0,r.jsx)(ev.Z,{children:"Extra litellm Params"}),(0,r.jsx)(ev.Z,{children:"Input Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Output Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Max Tokens"})]})}),(0,r.jsx)(eb.Z,{children:m.data.filter(e=>"all"===R||e.model_name===R||null==R||""===R).map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.model_name})}),(0,r.jsx)(e_.Z,{children:e.provider}),"Admin"===c&&(0,r.jsx)(e_.Z,{children:e.api_base}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Litellm params"})}),(0,r.jsx)(Q.Z,{children:(0,r.jsx)("pre",{children:JSON.stringify(e.cleanedLitellmParams,null,2)})})]})}),(0,r.jsx)(e_.Z,{children:e.input_cost}),(0,r.jsx)(e_.Z,{children:e.output_cost}),(0,r.jsx)(e_.Z,{children:e.max_tokens}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>U(e)}),(0,r.jsx)(e$,{modelID:e.model_info.id,accessToken:o})]})]},l))})]})})]}),(0,r.jsx)(e=>{let{visible:l,onCancel:t,model:s,onSubmit:a}=e,[n]=er.Z.useForm(),o={},i="",c="";if(s){o=s.litellm_params,i=s.model_name;let e=s.model_info;e&&(c=e.id,console.log("model_id: ".concat(c)),o.model_id=c)}return(0,r.jsx)(en.Z,{title:"Edit Model "+i,visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a(e),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:z,initialValues:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,r.jsx)($.Z,{})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"max_retries",name:"max_retries",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:I,onCancel:()=>{P(!1),E(null)},model:T,onSubmit:z})]}),(0,r.jsxs)(eB.Z,{className:"h-full",children:[(0,r.jsx)(e0,{level:2,children:"Add new model"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(er.Z,{form:g,onFinish:()=>{g.validateFields().then(e=>{e4(e,o,g)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.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,r.jsx)(eA.Z,{value:S.toString(),children:v.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>{K(e),A(e)},children:e},l))})}),(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"Vertex AI (Anthropic, Gemini, etc.)"===(s=S.toString())?"gemini-pro":"Anthropic"==s?"claude-3-opus":"Amazon Bedrock"==s?"claude-3-opus":"Gemini (Google AI Studio)"==s?"gemini-pro":"gpt-3.5-turbo"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"LiteLLM Model Name(s)",name:"model",tooltip:"Actual model name used for making litellm.completion() call.",className:"mb-0",children:"Azure"===S?(0,r.jsx)($.Z,{placeholder:"Enter model name"}):b.length>0?(0,r.jsx)(eK.Z,{value:b,children:b.map((e,l)=>(0,r.jsx)(eV.Z,{value:e,children:e},l))}):(0,r.jsx)($.Z,{placeholder:"gpt-3.5-turbo-0125"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Actual model name used for making ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," models with the same 'public name'"]})})]}),"Amazon Bedrock"!=S&&"Vertex AI (Anthropic, Gemini, etc.)"!=S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,r.jsx)($.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==S&&(0,r.jsx)(er.Z.Item,{label:"Organization ID",name:"organization_id",children:(0,r.jsx)($.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,r.jsx)($.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,r.jsx)($.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,r.jsx)(eQ.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;g.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?h.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&h.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,r.jsx)(ec.ZP,{icon:(0,r.jsx)(eX.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==S||"OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)"==S)&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,r.jsx)($.Z,{placeholder:"https://..."})}),"Azure"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Version",name:"api_version",children:(0,r.jsx)($.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==S&&(0,r.jsxs)(er.Z.Item,{label:"Base Model",name:"base_model",children:[(0,r.jsx)($.Z,{placeholder:"azure/gpt-3.5-turbo"}),(0,r.jsxs)(el.Z,{children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from ",(0,r.jsx)(e1,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})]}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"us-east-1"})}),(0,r.jsx)(er.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,r.jsx)(eY.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,r.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Model"})}),(0,r.jsx)(eJ.Z,{title:"Get help on our github",children:(0,r.jsx)(eR.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(el.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,r.jsx)(H.Z,{onClick:V,children:"Run `/health`"}),N&&(0,r.jsx)("pre",{children:JSON.stringify(N,null,2)})]})})]})]})})};let{Option:e5}=ea.default;var e3=e=>{let{userID:l,accessToken:t,teams:s}=e,[a]=er.Z.useForm(),[o,i]=(0,n.useState)(!1),[c,d]=(0,n.useState)(null),[m,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await v(t,l,"any"),s=[];for(let l=0;l{i(!1),a.resetFields()},p=()=>{i(!1),d(null),a.resetFields()},j=async e=>{try{h.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let s=await g(t,null,e);console.log("user create Response:",s),d(s.key),h.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Invite User"}),(0,r.jsxs)(en.Z,{title:"Invite User",visible:o,width:800,footer:null,onOk:x,onCancel:p,children:[(0,r.jsx)(el.Z,{className:"mb-1",children:"Invite a user to login to the Admin UI and create Keys"}),(0,r.jsx)(el.Z,{className:"mb-6",children:(0,r.jsx)("b",{children:"Note: SSO Setup Required for this"})}),(0,r.jsxs)(er.Z,{form:a,onFinish:j,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsx)(er.Z.Item,{label:"User Email",name:"user_email",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",children:(0,r.jsx)(ea.default,{placeholder:"Select Team ID",style:{width:"100%"},children:s?s.map(e=>(0,r.jsx)(e5,{value:e.team_id,children:e.team_alias},e.team_id)):(0,r.jsx)(e5,{value:null,children:"Default Team"},"default")})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create User"})})]})]}),c&&(0,r.jsxs)(en.Z,{title:"User Created Successfully",visible:o,onOk:x,onCancel:p,footer:null,children:[(0,r.jsx)("p",{children:"User has been created to access your proxy. Please Ask them to Log In."}),(0,r.jsx)("br",{}),(0,r.jsx)("p",{children:(0,r.jsx)("b",{children:"Note: This Feature is only supported through SSO on the Admin UI"})})]})]})},e6=e=>{let{accessToken:l,token:t,keys:s,userRole:a,userID:o,teams:i,setKeys:c}=e,[d,m]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(0),[j,g]=n.useState(null),[f,y]=(0,n.useState)(null);if((0,n.useEffect)(()=>{if(!l||!t||!a||!o)return;let e=async()=>{try{let e=await Z(l,null,a,!0,x,25);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};l&&t&&a&&o&&e();let s=async()=>{try{let e=await T(l,null);console.log("user data response:",e),u(e)}catch(e){console.error("There was an error fetching the model data",e)}};a&&("Admin"==a||"Admin Viewer"==a)&&!h&&s()},[l,t,a,o,x]),!d||!l||!t||!a||!o)return(0,r.jsx)("div",{children:"Loading..."});let w=async e=>{try{let t=await T(l,e);console.log("user data response:",t),u(t)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,r.jsx)("div",{style:{width:"100%"},children:(0,r.jsxs)(Y.Z,{className:"gap-2 p-2 h-[80vh] w-full mt-8",children:[(0,r.jsx)(e3,{userID:o,accessToken:l,teams:i}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[80vh] mb-4",children:[(0,r.jsxs)("div",{className:"mb-4 mt-1",children:[(0,r.jsxs)(el.Z,{children:[(0,r.jsx)("b",{children:"Key Owners: "})," Users on LiteLLM that created API Keys. Automatically tracked by LiteLLM"]}),(0,r.jsxs)(el.Z,{className:"mt-1",children:[(0,r.jsx)("b",{children:"End Users: "}),"End Users of your LLM API calls. Tracked When a `user` param is passed in your LLM calls"]})]}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Key Owners"}),(0,r.jsx)(eU.Z,{value:"2",children:"End-Users"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"User ID"}),(0,r.jsx)(ev.Z,{children:"User Email"}),(0,r.jsx)(ev.Z,{children:"User Models"}),(0,r.jsx)(ev.Z,{children:"User Spend ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User Max Budget ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User API Key Aliases"})]})}),(0,r.jsx)(eb.Z,{children:d.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_id}),(0,r.jsx)(e_.Z,{children:e.user_email}),(0,r.jsx)(e_.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,r.jsx)(e_.Z,{children:e.spend?e.spend:0}),(0,r.jsx)(e_.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(Y.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,r.jsx)(ep.Z,{size:"xs",color:"indigo",children:e.key_aliases.filter(e=>null!==e).join(", ")}):(0,r.jsx)(ep.Z,{size:"xs",color:"gray",children:"No Keys"})})})]},e.user_id))})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)("div",{className:"flex-1"}),(0,r.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,r.jsx)(el.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,r.jsx)(eA.Z,{defaultValue:"1",className:"w-3/4",children:null==s?void 0:s.map((e,l)=>{if(e&&null!==e.key_alias&&e.key_alias.length>0)return(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>w(e.token),children:e.key_alias},l)})})]})]}),(0,r.jsxs)(ew.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"End User"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Total Events"})]})}),(0,r.jsx)(eb.Z,{children:null==h?void 0:h.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.end_user}),(0,r.jsx)(e_.Z,{children:e.total_spend}),(0,r.jsx)(e_.Z,{children:e.total_events})]},l))})]})]})]})]})]}),function(){if(!d)return null;let e=Math.ceil(d.length/25);return(0,r.jsxs)("div",{className:"flex justify-between items-center",children:[(0,r.jsxs)("div",{children:["Showing Page ",x+1," of ",e]}),(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:0===x,onClick:()=>p(x-1),children:"← Prev"}),(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",onClick:()=>{p(x+1)},children:"Next →"})]})]})}()]})})},e7=e=>{let{teams:l,searchParams:t,accessToken:s,setTeams:a,userID:o,userRole:i}=e,[c]=er.Z.useForm(),[d]=er.Z.useForm(),{Title:m,Paragraph:u}=eR.default,[x,p]=(0,n.useState)(""),[j,g]=(0,n.useState)(!1),[f,Z]=(0,n.useState)(l?l[0]:null),[b,_]=(0,n.useState)(!1),[k,S]=(0,n.useState)(!1),[A,N]=(0,n.useState)([]),[C,I]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),[E,F]=(0,n.useState)({}),O=e=>{Z(e),g(!0)},M=async e=>{let t=e.team_id;if(console.log("handleEditSubmit:",e),null==s)return;let r=await U(s,e);l&&a(l.map(e=>e.team_id===t?r.data:e)),h.ZP.success("Team updated successfully"),g(!1),Z(null)},L=async e=>{T(e),I(!0)},D=async()=>{if(null!=P&&null!=l&&null!=s){try{await y(s,P);let e=l.filter(e=>e.team_id!==P);a(e)}catch(e){console.error("Error deleting the team:",e)}I(!1),T(null)}};(0,n.useEffect)(()=>{let e=async()=>{try{if(null===o||null===i||null===s||null===l)return;console.log("fetching team info:");let e={};for(let t=0;t<(null==l?void 0:l.length);t++){let a=l[t].team_id,r=await w(s,a);console.log("teamInfo response:",r),null!==r&&(e={...e,[a]:r})}F(e)}catch(e){console.error("Error fetching team info:",e)}};(async()=>{try{if(null===o||null===i)return;if(null!==s){let e=(await v(s,o,i)).data.map(e=>e.id);console.log("available_model_names:",e),N(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,o,i,l]);let B=async e=>{try{if(null!=s){h.ZP.info("Creating Team");let t=await R(s,e);null!==l?a([...l,t]):a([t]),console.log("response for team create call: ".concat(t)),h.ZP.success("Team created"),_(!1)}}catch(e){console.error("Error creating the team:",e),h.ZP.error("Error creating the team: "+e,20)}},q=async e=>{try{if(null!=s&&null!=l){h.ZP.info("Adding Member");let t={role:"user",user_email:e.user_email,user_id:e.user_id},r=await z(s,f.team_id,t);console.log("response for team create call: ".concat(r.data));let n=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(r.data.team_id)),e.team_id===r.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...l];e[n]=r.data,a(e),Z(r.data)}S(!1)}}catch(e){console.error("Error creating the team:",e)}};return console.log("received teams ".concat(l)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"All Teams"}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Team Name"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"}),(0,r.jsx)(ev.Z,{children:"Info"})]})}),(0,r.jsx)(eb.Z,{children:l&&l.length>0?l.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,r.jsx)(e_.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,r.jsxs)(el.Z,{children:["TPM:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].keys&&E[e.team_id].keys.length," Keys"]}),(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].team_info&&E[e.team_id].team_info.members_with_roles&&E[e.team_id].team_info.members_with_roles.length," Members"]})]}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>O(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>L(e.team_id),icon:ex.Z,size:"sm"})]})]},e.team_id)):null})]}),C&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:D,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{I(!1),T(null)},children:"Cancel"})]})]})]})})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>_(!0),children:"+ Create New Team"}),(0,r.jsx)(en.Z,{title:"Create Team",visible:b,width:800,footer:null,onOk:()=>{_(!1),c.resetFields()},onCancel:()=>{_(!1),c.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:B,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"Team Members"}),(0,r.jsx)(u,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>{Z(e)},children:e.team_alias},l))}):(0,r.jsxs)(u,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:f?f.members_with_roles.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.role})]},l)):null})]})}),f&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,team:s,onSubmit:a}=e,[n]=er.Z.useForm();return(0,r.jsx)(en.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a({...e,team_id:s.team_id}),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:M,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A&&A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:j,onCancel:()=>{g(!1),Z(null)},team:f,onSubmit:M})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto mb-5",onClick:()=>S(!0),children:"+ Add member"}),(0,r.jsx)(en.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{S(!1),d.resetFields()},onCancel:()=>{S(!1),d.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},e9=t(18190),le=e=>{let l,{searchParams:t,accessToken:s,showSSOBanner:a}=e,[o]=er.Z.useForm(),[i]=er.Z.useForm(),{Title:c,Paragraph:d}=eR.default,[m,u]=(0,n.useState)(""),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)(!1),[f,y]=(0,n.useState)(!1),[Z,w]=(0,n.useState)(!1),[b,_]=(0,n.useState)(!1),[k,v]=(0,n.useState)(!1);try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let S=()=>{v(!1)},A=["proxy_admin","proxy_admin_viewer"];(0,n.useEffect)(()=>{(async()=>{if(null!=s){let e=[],l=await M(s,"proxy_admin_viewer");l.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(l));let t=await M(s,"proxy_admin");t.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(t)),console.log("combinedList: ".concat(e)),p(e)}})()},[s]);let N=()=>{w(!1),i.resetFields()},C=()=>{w(!1),i.resetFields()},I=e=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]}),P=(e,l,t)=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,r.jsx)(eA.Z,{value:l,children:A.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:t,valuePropName:"user_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t,disabled:!0})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Update role"})})]}),T=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,null);console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),h.ZP.success("Refresh tab to see updated user role"),w(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),g(!1)}}catch(e){console.error("Error creating the key:",e)}},F=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call"),e.user_email,e.user_id;let l=await B(s,e,"proxy_admin");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),y(!1)}}catch(e){console.error("Error creating the key:",e)}},O=async e=>{null!=s&&G(s,{environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}})};return console.log("admins: ".concat(null==x?void 0:x.length)),(0,r.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,r.jsx)(c,{level:4,children:"Admin Access "}),(0,r.jsxs)(d,{children:[a&&(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc. ",(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:x?x.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.user_role}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>w(!0)}),(0,r.jsx)(en.Z,{title:"Update role",visible:Z,width:800,footer:null,onOk:N,onCancel:C,children:P(T,e.user_role,e.user_id)})]})]},l)):null})]})})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("div",{className:"flex justify-start",children:[(0,r.jsx)(H.Z,{className:"mr-4 mb-5",onClick:()=>y(!0),children:"+ Add admin"}),(0,r.jsx)(en.Z,{title:"Add admin",visible:f,width:800,footer:null,onOk:()=>{y(!1),i.resetFields()},onCancel:()=>{y(!1),i.resetFields()},children:I(F)}),(0,r.jsx)(H.Z,{className:"mb-5",onClick:()=>g(!0),children:"+ Add viewer"}),(0,r.jsx)(en.Z,{title:"Add viewer",visible:j,width:800,footer:null,onOk:()=>{g(!1),i.resetFields()},onCancel:()=>{g(!1),i.resetFields()},children:I(E)})]})})]}),(0,r.jsxs)(Y.Z,{children:[(0,r.jsx)(c,{level:4,children:"Add SSO"}),(0,r.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,r.jsx)(H.Z,{onClick:()=>_(!0),children:"Add SSO"}),(0,r.jsx)(en.Z,{title:"Add SSO",visible:b,width:800,footer:null,onOk:()=>{_(!1),o.resetFields()},onCancel:()=>{_(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{F(e),O(e),_(!1),v(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,r.jsx)(eo.Z.Password,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,r.jsx)(eo.Z.Password,{})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,r.jsxs)(en.Z,{title:"SSO Setup Instructions",visible:k,width:800,footer:null,onOk:S,onCancel:()=>{v(!1)},children:[(0,r.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{onClick:S,children:"Done"})})]})]}),(0,r.jsxs)(e9.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access ",(0,r.jsxs)("a",{href:l,target:"_blank",children:[(0,r.jsx)("b",{children:l})," "]})]})]})]})},ll=t(12224),lt=e=>{let{accessToken:l,userRole:t,userID:s}=e,[a,o]=(0,n.useState)([]),[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)(!1),[u]=er.Z.useForm(),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)(""),[Z,w]=(0,n.useState)({}),[b,_]=(0,n.useState)([]),k=e=>{b.includes(e)?_(b.filter(l=>l!==e)):_([...b,e])},v={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),o(e.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 t=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",t),_(e.active_alerts),y(t),w(e.alerts_to_webhook)}c(l)})},[l,t,s]);let S=e=>b&&b.includes(e),A=e=>{if(!l)return;let t=Object.fromEntries(Object.entries(e.variables).map(e=>{var l;let[t,s]=e;return[t,(null===(l=document.querySelector('input[name="'.concat(t,'"]')))||void 0===l?void 0:l.value)||s]}));console.log("updatedVariables",t),console.log("updateAlertTypes",j);try{G(l,{environment_variables:t})}catch(e){h.ZP.error("Failed to update callback: "+e,20)}h.ZP.success("Callback updated successfully")},N=()=>{l&&u.validateFields().then(e=>{if(console.log("Form values:",e),"langfuse"===e.callback){G(l,{environment_variables:{LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey},litellm_settings:{success_callback:[e.callback]}});let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:null,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey}};o(a?[...a,t]:[t])}else if("slack"===e.callback){console.log("values.slackWebhookUrl: ".concat(e.slackWebhookUrl)),G(l,{general_settings:{alerting:["slack"],alerting_threshold:300},environment_variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl}}),console.log("values.callback: ".concat(e.callback));let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:null,LANGFUSE_SECRET_KEY:null}};o(a?[...a,t]:[t])}m(!1),u.resetFields(),p(null)})};return l?(console.log("callbacks: ".concat(a)),(0,r.jsxs)("div",{className:"w-full mx-4",children:[(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Logging Callbacks"}),(0,r.jsx)(eU.Z,{value:"2",children:"Alerting"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Callback"}),(0,r.jsx)(ev.Z,{children:"Callback Env Vars"})]})}),(0,r.jsx)(eb.Z,{children:a.map((e,t)=>{var s;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ep.Z,{color:"emerald",children:e.name})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)("ul",{children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).filter(e=>{let[l,t]=e;return null!==t}).map(e=>{let[l,t]=e;return(0,r.jsxs)("li",{children:[(0,r.jsx)(el.Z,{className:"mt-2",children:l}),"LANGFUSE_HOST"===l?(0,r.jsx)("p",{children:"default value=https://cloud.langfuse.com"}):(0,r.jsx)("div",{}),(0,r.jsx)($.Z,{name:l,defaultValue:t,type:"password"})]},l)})}),(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>A(e),children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,e.name),className:"mx-2",children:"Test Callback"})]})]},t)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{console.log("Add callback clicked"),m(!0)},children:"Add Callback"})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(el.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from ",(0,r.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{children:"Slack Webhook URL"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(v).map((e,l)=>{let[t,s]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ll.Z,{id:"switch",name:"switch",checked:S(t),onChange:()=>k(t)})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:s})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:t,type:"password",defaultValue:Z&&Z[t]?Z[t]:f})})]},l)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(v).forEach(l=>{let[t,s]=l,a=document.querySelector('input[name="'.concat(t,'"]'));console.log("key",t),console.log("webhookInput",a);let r=(null==a?void 0:a.value)||"";console.log("newWebhookValue",r),e[t]=r}),console.log("updatedAlertToWebhooks",e);let t={general_settings:{alert_to_webhook_url:e,alert_types:b}};console.log("payload",t);try{G(l,t)}catch(e){h.ZP.error("Failed to update alerts: "+e,20)}h.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,"slack"),className:"mx-2",children:"Test Alerts"})]})})]})]})}),(0,r.jsx)(en.Z,{title:"Add Callback",visible:d,onOk:N,width:800,onCancel:()=>{m(!1),u.resetFields(),p(null)},footer:null,children:(0,r.jsxs)(er.Z,{form:u,layout:"vertical",onFinish:N,children:[(0,r.jsx)(er.Z.Item,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,r.jsx)(ea.default,{onChange:e=>{p(e)},children:(0,r.jsx)(ea.default.Option,{value:"langfuse",children:"langfuse"})})}),"langfuse"===x&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PUBLIC_KEY",name:"langfusePublicKey",rules:[{required:!0,message:"Please enter the public key"}],children:(0,r.jsx)($.Z,{type:"password"})}),(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PRIVATE_KEY",name:"langfusePrivateKey",rules:[{required:!0,message:"Please enter the private key"}],children:(0,r.jsx)($.Z,{type:"password"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:ls}=ea.default;var la=e=>{let{models:l,accessToken:t,routerSettings:s,setRouterSettings:a}=e,[o]=er.Z.useForm(),[i,c]=(0,n.useState)(!1),[d,m]=(0,n.useState)("");return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>c(!0),children:"+ Add Fallbacks"}),(0,r.jsx)(en.Z,{title:"Add Fallbacks",visible:i,width:800,footer:null,onOk:()=>{c(!1),o.resetFields()},onCancel:()=>{c(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{console.log(e);let{model_name:l,models:r}=e,n=[...s.fallbacks||[],{[l]:r}],i={...s,fallbacks:n};console.log(i);try{G(t,{router_settings:i}),a(i)}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully"),c(!1),o.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,r.jsx)(eA.Z,{defaultValue:d,children:l&&l.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsx)(eK.Z,{value:l,children:l&&l.filter(e=>e!=d).map(e=>(0,r.jsx)(eV.Z,{value:e,children:e},e))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},lr=t(12968);async function ln(e,l){console.log("isLocal:",!1);let t=window.location.origin,s=new lr.ZP.OpenAI({apiKey:l,baseURL:t,dangerouslyAllowBrowser:!0});try{let l=await s.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});h.ZP.success((0,r.jsxs)("span",{children:["Test model=",(0,r.jsx)("strong",{children:e}),", received model=",(0,r.jsx)("strong",{children:l.model}),". See ",(0,r.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lo=e=>{let{accessToken:l,userRole:t,userID:s,modelData:a}=e,[o,i]=(0,n.useState)({}),[c,d]=(0,n.useState)(!1),[m]=er.Z.useForm(),[u,x]=(0,n.useState)(null),p={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"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),i(e.router_settings)})},[l,t,s]);let j=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(o.fallbacks)),o.fallbacks.map(l=>(e in l&&delete l[e],l));try{await G(l,{router_settings:o}),i({...o}),h.ZP.success("Router settings updated successfully")}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}}},g=e=>{if(!l)return;console.log("router_settings",e);let t=Object.fromEntries(Object.entries(e).map(e=>{let[l,t]=e;if("routing_strategy_args"!==l){var s;return[l,(null===(s=document.querySelector('input[name="'.concat(l,'"]')))||void 0===s?void 0:s.value)||t]}return null}).filter(e=>null!==e));console.log("updatedVariables",t);try{G(l,{router_settings:t})}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully")};return l?(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"General Settings"}),(0,r.jsx)(eU.Z,{value:"2",children:"Fallbacks"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,r.jsx)(et.Z,{children:"Router Settings"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Setting"}),(0,r.jsx)(ev.Z,{children:"Value"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(o).filter(e=>{let[l,t]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l}).map(e=>{let[l,t]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(el.Z,{children:l}),(0,r.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:p[l]})]}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:l,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t.toString()})})]},l)})})]})}),(0,r.jsx)(J.Z,{children:(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>g(o),children:"Save Changes"})})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Model Name"}),(0,r.jsx)(ev.Z,{children:"Fallbacks"})]})}),(0,r.jsx)(eb.Z,{children:o.fallbacks&&o.fallbacks.map((e,t)=>Object.entries(e).map(e=>{let[s,a]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:s}),(0,r.jsx)(e_.Z,{children:Array.isArray(a)?a.join(", "):a}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(H.Z,{onClick:()=>ln(s,l),children:"Test Fallback"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(eZ.Z,{icon:ex.Z,size:"sm",onClick:()=>j(s)})})]},t.toString()+s)}))})]}),(0,r.jsx)(la,{models:(null==a?void 0:a.data)?a.data.map(e=>e.model_name):[],accessToken:l,routerSettings:o,setRouterSettings:i})]})]})]})}):null},li=t(67951),lc=e=>{let{}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsxs)("div",{className:"mb-5",children:[(0,r.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,r.jsx)(el.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,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{children:[(0,r.jsx)(eU.Z,{children:"OpenAI Python SDK"}),(0,r.jsx)(eU.Z,{children:"LlamaIndex"}),(0,r.jsx)(eU.Z,{children:"Langchain Py"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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 = "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 ld(e,l,t,s){console.log("isLocal:",!1);let a=window.location.origin,r=new lr.ZP.OpenAI({apiKey:s,baseURL:a,dangerouslyAllowBrowser:!0});try{for await(let s of(await r.chat.completions.create({model:t,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&l(s.choices[0].delta.content)}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lm=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)(""),[m,h]=(0,n.useState)([]),[u,x]=(0,n.useState)(void 0),[p,j]=(0,n.useState)([]);(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{let e=await v(l,a,s);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}));console.log(l),j(l),x(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,a,s]);let g=(e,l)=>{h(t=>{let s=t[t.length-1];return s&&s.role===e?[...t.slice(0,t.length-1),{role:e,content:s.content+l}]:[...t,{role:e,content:l}]})},f=async()=>{if(""!==c.trim()&&o&&t&&s&&a){h(e=>[...e,{role:"user",content:c}]);try{u&&await ld(c,e=>g("assistant",e),u,o)}catch(e){console.error("Error fetching model response",e),g("assistant","Error fetching model response")}d("")}};if(s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,r.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsx)(ez.Z,{children:(0,r.jsx)(eU.Z,{children:"Chat"})}),(0,r.jsx)(eq.Z,{children:(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)("div",{className:"sm:max-w-2xl",children:(0,r.jsxs)(Y.Z,{numItems:2,children:[(0,r.jsxs)(J.Z,{children:[(0,r.jsx)(el.Z,{children:"API Key"}),(0,r.jsx)($.Z,{placeholder:"Type API Key here",type:"password",onValueChange:i,value:o})]}),(0,r.jsxs)(J.Z,{className:"mx-2",children:[(0,r.jsx)(el.Z,{children:"Select Model:"}),(0,r.jsx)(ea.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),x(e)},options:p,style:{width:"200px"}})]})]})}),(0,r.jsxs)(ew.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,r.jsx)(ek.Z,{children:(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{})})}),(0,r.jsx)(eb.Z,{children:m.map((e,l)=>(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,r.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)($.Z,{type:"text",value:c,onChange:e=>d(e.target.value),placeholder:"Type your message..."}),(0,r.jsx)(H.Z,{onClick:f,className:"ml-2",children:"Send"})]})})]})})]})})})})},lh=t(33509),lu=t(95781);let{Sider:lx}=lh.default;var lp=e=>{let{setPage:l,userRole:t,defaultSelectedKey:s}=e;return"Admin Viewer"==t?(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,r.jsx)(lx,{width:120,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["4"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:"API Keys"},"4"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:"Models"},"2"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:"Chat UI"},"3"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1")]})})}):(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,r.jsx)(lx,{width:145,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["1"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:(0,r.jsx)(el.Z,{children:"API Keys"})},"1"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:(0,r.jsx)(el.Z,{children:"Test Key"})},"3"),"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:(0,r.jsx)(el.Z,{children:"Models"})},"2"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:(0,r.jsx)(el.Z,{children:"Usage"})},"4"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("teams"),children:(0,r.jsx)(el.Z,{children:"Teams"})},"6"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("users"),children:(0,r.jsx)(el.Z,{children:"Users"})},"5"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("settings"),children:(0,r.jsx)(el.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("general-settings"),children:(0,r.jsx)(el.Z,{children:"Router Settings"})},"9"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("admin-panel"),children:(0,r.jsx)(el.Z,{children:"Admin"})},"7"):null,(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api_ref"),children:(0,r.jsx)(el.Z,{children:"API Reference"})},"11")]})})})},lj=t(67989),lg=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)([]),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)([]),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),[b,v]=(0,n.useState)([]),[S,T]=(0,n.useState)([]),[O,M]=(0,n.useState)([]),[R,L]=(0,n.useState)([]),[U,D]=(0,n.useState)(null),z=new Date(o.getFullYear(),o.getMonth(),1),B=new Date(o.getFullYear(),o.getMonth()+1,0),q=V(z),K=V(B);function V(e){let l=e.getFullYear(),t=e.getMonth()+1,s=e.getDate();return"".concat(l,"-").concat(t<10?"0"+t:t,"-").concat(s<10?"0"+s:s)}console.log("Start date is ".concat(q)),console.log("End date is ".concat(K)),(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{if(console.log("user role: ".concat(s)),"Admin"==s||"Admin Viewer"==s){let e=await I(l);c(e);let t=(await P(l)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,10),spend:e.total_spend}));m(t);let r=(await E(l)).map(e=>({key:e.model,spend:e.total_spend}));u(r);let n=await A(l);console.log("teamSpend",n),g(n.daily_spend),w(n.teams);let o=n.total_spend_per_team;o=o.map(e=>(e.name=e.team_id||"",e.value=e.total_spend||0,e)),v(o);let i=await N(l);y(i.top_10_tags);let d=(await _(l,a,s)).data;console.log("model groups in model dashboard",d);let h=[];for(let e=0;e{if(console.log("result from spend logs call",e),"daily_spend"in e){let l=e.daily_spend;console.log("daily spend",l),c(l);let t=e.top_api_keys;m(t)}else{let t=(await F(l,function(e){let l=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[t,s]=e;"spend"!==t&&"startTime"!==t&&"models"!==t&&"users"!==t&&l.push({key:t,spend:s})})}),l.sort((e,l)=>Number(l.spend)-Number(e.spend));let t=l.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(t[0]))),t}(e))).info.map(e=>({key:(e.key_name||e.key_alias).substring(0,10),spend:e.spend}));m(t),p(function(e){let l={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[t,s]=e;""!==t&&null!=t&&"None"!=t&&(l[t]||(l[t]=0),l[t]+=s)})});let t=Object.entries(l).map(e=>{let[l,t]=e;return{user_id:l,spend:t}});t.sort((e,l)=>l.spend-e.spend);let s=t.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)}});let e=await k(l,a,s,null);console.log("Model metrics response:",e);let r=[...e].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(e),M(r)}catch(e){console.error("There was an error fetching the data",e)}})()},[l,t,s,a,q,K]);let G=async e=>{if(console.log("Updating model metrics for group:",e),l&&a&&s){D(e);try{let t=await k(l,a,s,e);console.log("Model metrics response:",t);let r=[...t].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(t),M(r)}catch(e){console.error("Failed to fetch model metrics",e)}}};return(0,r.jsxs)("div",{style:{width:"100%"},className:"p-8",children:[(0,r.jsx)(eE,{userID:a,userRole:s,accessToken:l,userSpend:null,selectedTeam:null}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{className:"mt-2",children:[(0,r.jsx)(eU.Z,{children:"All Up"}),(0,r.jsx)(eU.Z,{children:"Team Based Usage"}),(0,r.jsx)(eU.Z,{children:"Tag Based Usage"}),(0,r.jsx)(eU.Z,{children:"Model Based Usage"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Monthly Spend"}),(0,r.jsx)(ej.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top API Keys"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Users"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Models"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:h,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsxs)(J.Z,{numColSpan:2,children:[(0,r.jsxs)(eg.Z,{className:"mb-2",children:[(0,r.jsx)(et.Z,{children:"Total Spend Per Team"}),(0,r.jsx)(lj.Z,{data:b})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Daily Spend Per Team"}),(0,r.jsx)(ej.Z,{className:"h-72",data:j,showLegend:!0,index:"date",categories:Z,yAxisWidth:80,colors:["blue","green","yellow","red","purple"],stack:!0})]})]}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Spend Per Tag - Last 30 Days"}),(0,r.jsxs)(el.Z,{children:["Get Started Tracking cost per tag ",(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/enterprise#tracking-spend-for-custom-tags",target:"_blank",children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Tag"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Requests"})]})}),(0,r.jsx)(eb.Z,{children:f.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.name}),(0,r.jsx)(e_.Z,{children:e.value}),(0,r.jsx)(e_.Z,{children:e.log_count})]},e.name))})]})]})}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)(et.Z,{children:"Filter By Model Group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080"},children:"View how requests were load balanced within a model group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080",fontStyle:"italic"},children:"(Beta feature) only supported for Azure Model Groups"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2",defaultValue:"all",children:[(0,r.jsx)(eN.Z,{value:"all",onClick:()=>G(null),children:"All Model Groups"}),R.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>G(e),children:e},l))]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Number Requests per Model"}),(0,r.jsx)(ej.Z,{data:S,className:"h-[50vh]",index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,r.jsxs)(eg.Z,{className:"mt-4",children:[(0,r.jsx)(et.Z,{children:"Latency Per Model"}),(0,r.jsx)(ej.Z,{data:O,className:"h-[50vh]",index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})]})]})]})},lf=()=>{let{Title:e,Paragraph:l}=eR.default,[t,s]=(0,n.useState)(""),[a,i]=(0,n.useState)(null),[c,d]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(!0),j=(0,o.useSearchParams)(),[g,f]=(0,n.useState)({data:[]}),y=j.get("userID"),Z=j.get("token"),[w,b]=(0,n.useState)("api-keys"),[_,k]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(Z){let e=(0,eM.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(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"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),s(l),"Admin Viewer"==l&&b("usage")}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[Z]),(0,r.jsx)(n.Suspense,{fallback:(0,r.jsx)("div",{children:"Loading..."}),children:(0,r.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,r.jsx)(m,{userID:y,userRole:t,userEmail:a,showSSOBanner:x}),(0,r.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,r.jsx)("div",{className:"mt-8",children:(0,r.jsx)(lp,{setPage:b,userRole:t,defaultSelectedKey:null})}),"api-keys"==w?(0,r.jsx)(eL,{userID:y,userRole:t,teams:c,keys:h,setUserRole:s,userEmail:a,setUserEmail:i,setTeams:d,setKeys:u}):"models"==w?(0,r.jsx)(e8,{userID:y,userRole:t,token:Z,accessToken:_,modelData:g,setModelData:f}):"llm-playground"==w?(0,r.jsx)(lm,{userID:y,userRole:t,token:Z,accessToken:_}):"users"==w?(0,r.jsx)(e6,{userID:y,userRole:t,token:Z,keys:h,teams:c,accessToken:_,setKeys:u}):"teams"==w?(0,r.jsx)(e7,{teams:c,setTeams:d,searchParams:j,accessToken:_,userID:y,userRole:t}):"admin-panel"==w?(0,r.jsx)(le,{setTeams:d,searchParams:j,accessToken:_,showSSOBanner:x}):"api_ref"==w?(0,r.jsx)(lc,{}):"settings"==w?(0,r.jsx)(lt,{userID:y,userRole:t,accessToken:_}):"general-settings"==w?(0,r.jsx)(lo,{userID:y,userRole:t,accessToken:_,modelData:g}):(0,r.jsx)(lg,{userID:y,userRole:t,token:Z,accessToken:_})]})]})})}}},function(e){e.O(0,[447,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-781ca5f151d78d1d.js b/ui/litellm-dashboard/out/_next/static/chunks/app/page-781ca5f151d78d1d.js deleted file mode 100644 index 40715cd067c..00000000000 --- a/ui/litellm-dashboard/out/_next/static/chunks/app/page-781ca5f151d78d1d.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{20661:function(e,l,t){Promise.resolve().then(t.bind(t,27125))},27125:function(e,l,t){"use strict";t.r(l),t.d(l,{default:function(){return lf}});var s,a,r=t(3827),n=t(64090),o=t(47907),i=t(8792),c=t(40491),d=t(65270),m=e=>{let{userID:l,userRole:t,userEmail:s,showSSOBanner:a}=e;console.log("User ID:",l),console.log("userEmail:",s),console.log("showSSOBanner:",a);let n=[{key:"1",label:(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("p",{children:["Role: ",t]}),(0,r.jsxs)("p",{children:["ID: ",l]})]})}];return(0,r.jsxs)("nav",{className:"left-0 right-0 top-0 flex justify-between items-center h-12 mb-4",children:[(0,r.jsx)("div",{className:"text-left my-2 absolute top-0 left-0",children:(0,r.jsx)("div",{className:"flex flex-col items-center",children:(0,r.jsx)(i.default,{href:"/",children:(0,r.jsx)("button",{className:"text-gray-800 rounded text-center",children:(0,r.jsx)("img",{src:"/get_image",width:160,height:160,alt:"LiteLLM Brand",className:"mr-2"})})})})}),(0,r.jsxs)("div",{className:"text-right mx-4 my-2 absolute top-0 right-0 flex items-center justify-end space-x-2",children:[a?(0,r.jsx)("div",{style:{padding:"6px",borderRadius:"8px"},children:(0,r.jsx)("a",{href:"https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat",target:"_blank",style:{fontSize:"14px",textDecoration:"underline"},children:"Request hosted proxy"})}):null,(0,r.jsx)("div",{style:{border:"1px solid #391085",padding:"6px",borderRadius:"8px"},children:(0,r.jsx)(c.Z,{menu:{items:n},children:(0,r.jsx)(d.Z,{children:s})})})]})]})},h=t(80588);let u=async()=>{try{let e=await fetch("https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"),l=await e.json();return console.log("received data: ".concat(l)),l}catch(e){throw console.error("Failed to get model cost map:",e),e}},x=async(e,l)=>{try{let t=await fetch("/model/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model created successfully. Wait 60s and refresh on 'All Models' page"),s}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,l)=>{console.log("model_id in model delete call: ".concat(l));try{let t=await fetch("/model/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),h.ZP.success("Model deleted successfully. Restart server to see this."),s}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/key/generate",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,l,t)=>{try{if(console.log("Form Values in keyCreateCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw h.ZP.error("Failed to parse metadata: "+e,20),Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let s=await fetch("/user/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:l,...t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,l)=>{try{console.log("in keyDeleteCall:",l);let t=await fetch("/key/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,l)=>{try{console.log("in teamDeleteCall:",l);let t=await fetch("/team/delete",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[l]})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete team: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to delete key:",e),e}},Z=async function(e,l,t){let s=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,r=arguments.length>5?arguments[5]:void 0;try{let n="/user/info";"App Owner"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),"App User"==t&&l&&(n="".concat(n,"?user_id=").concat(l)),console.log("in userInfoCall viewAll=",s),s&&r&&null!=a&&void 0!=a&&(n="".concat(n,"?view_all=true&page=").concat(a,"&page_size=").concat(r));let o=await fetch(n,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let i=await o.json();return console.log("API Response:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},w=async(e,l)=>{try{let t="/team/info";l&&(t="".concat(t,"?team_id=").concat(l)),console.log("in teamInfoCall");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},b=async e=>{try{let l=await fetch("/global/spend",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,l,t)=>{try{let l=await fetch("/v2/model/info",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log("modelInfoCall:",t),t}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,l,t,s)=>{try{let l="/model/metrics";s&&(l="".concat(l,"?_selected_model_group=").concat(s));let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,l,t)=>{try{let l=await fetch("/models",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to create key:",e),e}},S=async(e,l)=>{try{let t="/global/spend/logs";console.log("in keySpendLogsCall:",t);let s=await fetch("".concat(t,"?api_key=").concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let l="/global/spend/teams";console.log("in teamSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},N=async e=>{try{let l="/global/spend/tags";console.log("in tagsSpendLogsCall:",l);let t=await fetch("".concat(l),{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},C=async(e,l,t,s,a,r)=>{try{console.log("user role in spend logs call: ".concat(t));let l="/spend/logs";l="App Owner"==t?"".concat(l,"?user_id=").concat(s,"&start_date=").concat(a,"&end_date=").concat(r):"".concat(l,"?start_date=").concat(a,"&end_date=").concat(r);let n=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let o=await n.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},I=async e=>{try{let l=await fetch("/global/spend/logs",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{let l=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,l)=>{try{l&&JSON.stringify({api_key:l});let t={method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}};l&&(t.body=JSON.stringify({api_key:l}));let s=await fetch("/global/spend/end_users",t);if(!s.ok){let e=await s.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},E=async e=>{try{let l=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let t=await l.json();return console.log(t),t}catch(e){throw console.error("Failed to create key:",e),e}},F=async(e,l)=>{try{let t=await fetch("/v2/key/info",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},O=async e=>{try{let l="/user/get_requests";console.log("in userGetRequesedtModelsCall:",l);let t=await fetch(l,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to get requested models:",e),e}},M=async(e,l)=>{try{let t="/user/get_users?role=".concat(l);console.log("in userGetAllUsersCall:",t);let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to delete key: "+e,20),Error("Network response was not ok")}let a=await s.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},R=async(e,l)=>{try{console.log("Form Values in teamCreateCall:",l);let t=await fetch("/team/new",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,l)=>{try{console.log("Form Values in keyUpdateCall:",l);let t=await fetch("/key/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update key Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,l)=>{try{console.log("Form Values in teamUpateCall:",l);let t=await fetch("/team/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update team: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update Team Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async(e,l)=>{try{console.log("Form Values in modelUpateCall:",l);let t=await fetch("/model/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error("Failed to update model: "+e,20),console.error("Error update from the server:",e),Error("Network response was not ok")}let s=await t.json();return console.log("Update model Response:",s),s}catch(e){throw console.error("Failed to update model:",e),e}},z=async(e,l,t)=>{try{console.log("Form Values in teamMemberAddCall:",t);let s=await fetch("/team/member_add",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:l,member:t})});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await s.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,l,t)=>{try{console.log("Form Values in userUpdateUserCall:",l);let s={...l};null!==t&&(s.user_role=t),s=JSON.stringify(s);let a=await fetch("/user/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:s});if(!a.ok){let e=await a.text();throw h.ZP.error("Failed to create key: "+e,20),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await a.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,l)=>{try{let t=await fetch("/global/predict/spend/logs",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({data:l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}let s=await t.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,l)=>{try{let t="/health/services?service=".concat(l);console.log("Checking Slack Budget Alerts service health");let s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw h.ZP.error("Failed ".concat(l," service health check ")+e),Error(e)}let a=await s.json();return h.ZP.success("Test request to ".concat(l," made - check logs/alerts on ").concat(l," to verify")),a}catch(e){throw console.error("Failed to perform health check:",e),e}},V=async(e,l,t)=>{try{let l=await fetch("/get/config/callbacks",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},G=async(e,l)=>{try{let t=await fetch("/config/update",{method:"POST",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...l})});if(!t.ok){let e=await t.text();throw h.ZP.error(e,20),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},W=async e=>{try{let l=await fetch("/health",{method:"GET",headers:{Authorization:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!l.ok){let e=await l.text();throw h.ZP.error(e),Error("Network response was not ok")}return await l.json()}catch(e){throw console.error("Failed to call /health:",e),e}};var J=t(10384),Y=t(46453),H=t(2179),$=t(52273),X=t(26780),Q=t(15595),ee=t(6698),el=t(71801),et=t(42440),es=t(42308),ea=t(50670),er=t(81583),en=t(99129),eo=t(44839),ei=t(88707),ec=t(1861);let{Option:ed}=ea.default;var em=e=>{let{userID:l,team:t,userRole:s,accessToken:a,data:o,setData:i}=e,[c]=er.Z.useForm(),[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(null),[p,g]=(0,n.useState)(null),[f,y]=(0,n.useState)([]),Z=()=>{m(!1),c.resetFields()},w=()=>{m(!1),x(null),c.resetFields()};(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===s)return;if(null!==a){let e=(await v(a,l,s)).data.map(e=>e.id);console.log("available_model_names:",e),y(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,s]);let b=async e=>{try{h.ZP.info("Making API Call"),m(!0);let t=await j(a,l,e);console.log("key create Response:",t),i(e=>e?[...e,t]:[t]),x(t.key),g(t.soft_budget),h.ZP.success("API Key Created"),c.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the key:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>m(!0),children:"+ Create New Key"}),(0,r.jsx)(en.Z,{title:"Create Key",visible:d,width:800,footer:null,onOk:Z,onCancel:w,children:(0,r.jsxs)(er.Z,{form:c,onFinish:b,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",hidden:!0,initialValue:t?t.team_id:null,valuePropName:"team_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t?t.team_alias:"",disabled:!0})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},onChange:e=>{e.includes("all-team-models")&&c.setFieldsValue({models:["all-team-models"]})},children:[(0,r.jsx)(ed,{value:"all-team-models",children:"All Team Models"},"all-team-models"),t&&t.models?t.models.includes("all-proxy-models")?f.map(e=>(0,r.jsx)(ed,{value:e,children:e},e)):t.models.map(e=>(0,r.jsx)(ed,{value:e,children:e},e)):f.map(e=>(0,r.jsx)(ed,{value:e,children:e},e))]})}),(0,r.jsxs)(X.Z,{className:"mt-20 mb-8",children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)("b",{children:"Optional Settings"})}),(0,r.jsxs)(Q.Z,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: $".concat((null==t?void 0:t.max_budget)!==null&&(null==t?void 0:t.max_budget)!==void 0?null==t?void 0:t.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.max_budget&&l>t.max_budget)throw Error("Budget cannot exceed team max budget: $".concat(t.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Reset Budget",name:"budget_duration",help:"Team Reset Budget: ".concat((null==t?void 0:t.budget_duration)!==null&&(null==t?void 0:t.budget_duration)!==void 0?null==t?void 0:t.budget_duration:"None"),children:(0,r.jsxs)(ea.default,{defaultValue:null,placeholder:"n/a",children:[(0,r.jsx)(ea.default.Option,{value:"24h",children:"daily"}),(0,r.jsx)(ea.default.Option,{value:"30d",children:"monthly"})]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Tokens per minute Limit (TPM)",name:"tpm_limit",help:"TPM cannot exceed team TPM limit: ".concat((null==t?void 0:t.tpm_limit)!==null&&(null==t?void 0:t.tpm_limit)!==void 0?null==t?void 0:t.tpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.tpm_limit&&l>t.tpm_limit)throw Error("TPM limit cannot exceed team TPM limit: ".concat(t.tpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Requests per minute Limit (RPM)",name:"rpm_limit",help:"RPM cannot exceed team RPM limit: ".concat((null==t?void 0:t.rpm_limit)!==null&&(null==t?void 0:t.rpm_limit)!==void 0?null==t?void 0:t.rpm_limit:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&t&&null!==t.rpm_limit&&l>t.rpm_limit)throw Error("RPM limit cannot exceed team RPM limit: ".concat(t.rpm_limit))}}],children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Expire Key (eg: 30s, 30h, 30d)",name:"duration",className:"mt-8",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})})]})]})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Key"})})]})}),u&&(0,r.jsx)(en.Z,{visible:d,onOk:Z,onCancel:w,footer:null,children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Save your Key"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Please save this secret key somewhere safe and accessible. For security reasons, ",(0,r.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,r.jsx)(J.Z,{numColSpan:1,children:null!=u?(0,r.jsxs)("div",{children:[(0,r.jsx)(el.Z,{className:"mt-3",children:"API Key:"}),(0,r.jsx)("div",{style:{background:"#f8f8f8",padding:"10px",borderRadius:"5px",marginBottom:"10px"},children:(0,r.jsx)("pre",{style:{wordWrap:"break-word",whiteSpace:"normal"},children:u})}),(0,r.jsx)(es.CopyToClipboard,{text:u,onCopy:()=>{h.ZP.success("API Key copied to clipboard")},children:(0,r.jsx)(H.Z,{className:"mt-3",children:"Copy API Key"})})]}):(0,r.jsx)(el.Z,{children:"Key being created, this might take 30s"})})]})})]})},eh=t(9454),eu=t(98941),ex=t(33393),ep=t(5),ej=t(9853),eg=t(13810),ef=t(39290),ey=t(66952),eZ=t(61244),ew=t(10827),eb=t(3851),e_=t(2044),ek=t(64167),ev=t(74480),eS=t(7178),eA=t(95093),eN=t(27166);let{Option:eC}=ea.default;var eI=e=>{let{userID:l,userRole:t,accessToken:s,selectedTeam:a,data:o,setData:i,teams:c}=e,[d,m]=(0,n.useState)(!1),[u,x]=(0,n.useState)(!1),[p,j]=(0,n.useState)(null),[g,y]=n.useState(null),[Z,w]=(0,n.useState)(null),[b,_]=(0,n.useState)(null),[k,A]=(0,n.useState)(""),[N,C]=(0,n.useState)(!1),[I,P]=(0,n.useState)(null),[T,E]=(0,n.useState)([]),F=new Set,[O,M]=(0,n.useState)(F);(0,n.useEffect)(()=>{(async()=>{try{if(null===l)return;if(null!==s&&null!==t){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),E(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[s,l,t]),(0,n.useEffect)(()=>{if(c){let e=new Set;c.forEach((l,t)=>{let s=l.team_id;e.add(s)}),M(e)}},[c]);let R=e=>{console.log("handleEditClick:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),P(e),C(!0)},U=async e=>{if(null==s)return;let l=e.token;e.key=l,console.log("handleEditSubmit:",e);let t=await L(s,e);console.log("handleEditSubmit: newKeyValues",t),o&&i(o.map(e=>e.token===l?t:e)),h.ZP.success("Key updated successfully"),C(!1),P(null)},D=async e=>{try{if(null==s||null==e)return;console.log("accessToken: ".concat(s,"; token: ").concat(e.token));let l=await S(s,e.token);console.log("Response:",l),_(l);try{let e=await q(s,l);console.log("Response2:",e);let t=[...l,...e.response];_(t),A(e.predicted_spend),console.log("Combined Data:",t)}catch(e){console.error("There was an error fetching the predicted data",e)}}catch(e){console.error("There was an error fetching the data",e)}};(0,n.useEffect)(()=>{D(Z)},[Z]);let z=async e=>{console.log("handleDelete:",e),null==e.token&&null!==e.token_id&&(e.token=e.token_id),null!=o&&(j(e.token),localStorage.removeItem("userData"+l),x(!0))},B=async()=>{if(null!=p&&null!=o){try{await f(s,p);let e=o.filter(e=>e.token!==p);i(e)}catch(e){console.error("Error deleting the key:",e)}x(!1),j(null)}};if(null!=o)return console.log("RERENDER TRIGGERED"),(0,r.jsxs)("div",{children:[(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh] mb-4 mt-2",children:[(0,r.jsxs)(ew.Z,{className:"mt-5 max-h-[300px] min-h-[300px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Key Alias"}),(0,r.jsx)(ev.Z,{children:"Secret Key"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"})]})}),(0,r.jsx)(eb.Z,{children:o.map(e=>{if(console.log(e),"litellm-dashboard"===e.team_id)return null;if(a){if(console.log("item team id: ".concat(e.team_id,", knownTeamIDs.has(item.team_id): ").concat(O.has(e.team_id),", selectedTeam id: ").concat(a.team_id)),(null!=a.team_id||null===e.team_id||O.has(e.team_id))&&e.team_id!=a.team_id)return null;console.log("item team id: ".concat(e.team_id,", is returned"))}return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"2px",whiteSpace:"pre-wrap",overflow:"hidden"},children:null!=e.key_alias?(0,r.jsx)(el.Z,{children:e.key_alias}):(0,r.jsx)(el.Z,{children:"Not Set"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.key_name})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:(()=>{try{return parseFloat(e.spend).toFixed(4)}catch(l){return e.spend}})()})}),(0,r.jsx)(e_.Z,{children:null!=e.max_budget?(0,r.jsx)(el.Z,{children:e.max_budget}):(0,r.jsx)(el.Z,{children:"Unlimited"})}),(0,r.jsx)(e_.Z,{children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(r.Fragment,{children:a&&a.models&&a.models.length>0?a.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l)):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:"all-proxy-models"})})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):"all-team-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Team Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(el.Z,{children:["TPM: ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{})," RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{onClick:()=>{w(e),y(e.id)},icon:eh.Z,size:"sm"}),(0,r.jsx)(ef.Z,{open:null!==g,onClose:()=>{y(null),w(null)},children:(0,r.jsx)(ey.Z,{children:Z&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsxs)("div",{className:"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3",children:[(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Spend"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:(()=>{try{return parseFloat(Z.spend).toFixed(4)}catch(e){return Z.spend}})()})})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Budget"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor font-semibold text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.max_budget?(0,r.jsx)(r.Fragment,{children:Z.max_budget}):(0,r.jsx)(r.Fragment,{children:"Unlimited"})})})]},e.name),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)("p",{className:"text-tremor-default font-medium text-tremor-content dark:text-dark-tremor-content",children:"Expires"}),(0,r.jsx)("div",{className:"mt-2 flex items-baseline space-x-2.5",children:(0,r.jsx)("p",{className:"text-tremor-default font-small text-tremor-content-strong dark:text-dark-tremor-content-strong",children:null!=Z.expires?(0,r.jsx)(r.Fragment,{children:new Date(Z.expires).toLocaleString(void 0,{day:"numeric",month:"long",year:"numeric",hour:"numeric",minute:"numeric",second:"numeric"})}):(0,r.jsx)(r.Fragment,{children:"Never"})})})]},e.name)]}),(0,r.jsx)(eg.Z,{className:"mt-6 mb-6",children:b&&(0,r.jsx)(ej.Z,{className:"mt-6",data:b,colors:["blue","amber"],index:"date",categories:["spend","predicted_spend"],yAxisWidth:80})}),(0,r.jsx)(et.Z,{children:"Metadata"}),(0,r.jsx)(el.Z,{children:JSON.stringify(Z.metadata)}),(0,r.jsx)(H.Z,{variant:"light",className:"mx-auto flex items-center",onClick:()=>{y(null),w(null)},children:"Close"})]})})}),(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>R(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>z(e),icon:ex.Z,size:"sm"})]})]},e.token)})})]}),u&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Key"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this key ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:B,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{x(!1),j(null)},children:"Cancel"})]})]})]})})]}),I&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,token:s,onSubmit:o}=e,[i]=er.Z.useForm(),[d,m]=(0,n.useState)(a),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)(!1);return(0,r.jsx)(en.Z,{title:"Edit Key",visible:l,width:800,footer:null,onOk:()=>{i.validateFields().then(e=>{i.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:i,onFinish:U,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Key Name",name:"key_alias",rules:[{required:!0,message:"Please input a key name"}],help:"required",children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",rules:[{validator:(e,l)=>{let t=l.filter(e=>!d.models.includes(e)&&"all-team-models"!==e&&"all-proxy-models"!==e&&!d.models.includes("all-proxy-models"));return(console.log("errorModels: ".concat(t)),t.length>0)?Promise.reject("Some models are not part of the new team's models - ".concat(t,"Team models: ").concat(d.models)):Promise.resolve()}}],children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(eC,{value:"all-team-models",children:"All Team Models"},"all-team-models"),d&&d.models?d.models.includes("all-proxy-models")?T.filter(e=>"all-proxy-models"!==e).map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):d.models.map(e=>(0,r.jsx)(eC,{value:e,children:e},e)):T.map(e=>(0,r.jsx)(eC,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"Max Budget (USD)",name:"max_budget",help:"Budget cannot exceed team max budget: ".concat((null==d?void 0:d.max_budget)!==null&&(null==d?void 0:d.max_budget)!==void 0?null==d?void 0:d.max_budget:"unlimited"),rules:[{validator:async(e,l)=>{if(l&&d&&null!==d.max_budget&&l>d.max_budget)throw console.log("keyTeam.max_budget: ".concat(d.max_budget)),Error("Budget cannot exceed team max budget: $".concat(d.max_budget))}}],children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"token",name:"token",hidden:!0}),(0,r.jsx)(er.Z.Item,{label:"Team",name:"team_id",help:"the team this key belongs to",children:(0,r.jsx)(eA.Z,{value:s.team_alias,children:null==c?void 0:c.map((e,l)=>(0,r.jsx)(eN.Z,{value:e.team_id,onClick:()=>m(e),children:e.team_alias},l))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Key"})})]})})},{visible:N,onCancel:()=>{C(!1),P(null)},token:I,onSubmit:U})]})},eP=t(76032),eT=t(35152),eE=e=>{let{userID:l,userRole:t,accessToken:s,userSpend:a,selectedTeam:o}=e;console.log("userSpend: ".concat(a));let[i,c]=(0,n.useState)(null!==a?a:0),[d,m]=(0,n.useState)(0),[h,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{let e=async()=>{if(s&&l&&t&&"Admin"===t&&null==a)try{let e=await b(s);e&&(e.spend?c(e.spend):c(0),e.max_budget?m(e.max_budget):m(0))}catch(e){console.error("Error fetching global spend data:",e)}};(async()=>{try{if(null===l||null===t)return;if(null!==s){let e=(await v(s,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),u(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[t,s,l]),(0,n.useEffect)(()=>{null!==a&&c(a)},[a]);let x=[];o&&o.models&&(x=o.models),x&&x.includes("all-proxy-models")?(console.log("user models:",h),x=h):x&&x.includes("all-team-models")?x=o.models:x&&0===x.length&&(x=h);let p=void 0!==i?i.toFixed(4):null;return console.log("spend in view user spend: ".concat(i)),(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsxs)("div",{children:[(0,r.jsxs)("p",{className:"text-tremor-default text-tremor-content dark:text-dark-tremor-content",children:["Total Spend"," "]}),(0,r.jsxs)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:["$",p]})]}),(0,r.jsx)("div",{className:"ml-auto",children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Team Models"})}),(0,r.jsx)(Q.Z,{className:"absolute right-0 z-10 bg-white p-2 shadow-lg max-w-xs",children:(0,r.jsx)(eP.Z,{children:x.map(e=>(0,r.jsx)(eT.Z,{children:(0,r.jsx)(el.Z,{children:e})},e))})})]})})]})},eF=e=>{let{userID:l,userRole:t,selectedTeam:s,accessToken:a}=e,[o,i]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{if(null===l||null===t)return;if(null!==a){let e=(await v(a,l,t)).data.map(e=>e.id);console.log("available_model_names:",e),i(e)}}catch(e){console.error("Error fetching user models:",e)}})()},[a,l,t]);let c=[];return s&&s.models&&(c=s.models),c&&c.includes("all-proxy-models")&&(console.log("user models:",o),c=o),(0,r.jsx)(r.Fragment,{children:(0,r.jsx)("div",{className:"mb-5",children:(0,r.jsx)("p",{className:"text-3xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:null==s?void 0:s.team_alias})})})},eO=e=>{let l,{teams:t,setSelectedTeam:s,userRole:a}=e,o={models:[],team_id:null,team_alias:"Default Team"},[i,c]=(0,n.useState)(o);return(l="App User"===a?t:t?[...t,o]:[o],"App User"===a)?null:(0,r.jsxs)("div",{className:"mt-5 mb-5",children:[(0,r.jsx)(et.Z,{children:"Select Team"}),(0,r.jsx)(el.Z,{children:"If you belong to multiple teams, this setting controls which team is used by default when creating new API Keys."}),(0,r.jsxs)(el.Z,{className:"mt-3 mb-3",children:[(0,r.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,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>s(e),children:e.team_alias},l))}):(0,r.jsxs)(el.Z,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]})},eM=t(37963),eR=t(36083);console.log("isLocal:",!1);var eL=e=>{let{userID:l,userRole:t,teams:s,keys:a,setUserRole:i,userEmail:c,setUserEmail:d,setTeams:m,setKeys:h}=e,[u,x]=(0,n.useState)(null),p=(0,o.useSearchParams)();p.get("viewSpend"),(0,o.useRouter)();let j=p.get("token"),[g,f]=(0,n.useState)(null),[y,w]=(0,n.useState)(null),[_,k]=(0,n.useState)([]),S={models:[],team_alias:"Default Team",team_id:null},[A,N]=(0,n.useState)(s?s[0]:S);if(window.addEventListener("beforeunload",function(){sessionStorage.clear()}),(0,n.useEffect)(()=>{if(j){let e=(0,eM.o)(j);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),f(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";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),i(l)}else console.log("User role not defined");e.user_email?d(e.user_email):console.log("User Email is not set ".concat(e))}}if(l&&g&&t&&!a&&!u){let e=sessionStorage.getItem("userModels"+l);e?k(JSON.parse(e)):(async()=>{try{let e=await Z(g,l,t,!1,null,null);if(console.log("received teams in user dashboard: ".concat(Object.keys(e),"; team values: ").concat(Object.entries(e.teams))),"Admin"==t){let e=await b(g);x(e),console.log("globalSpend:",e)}else x(e.user_info);h(e.keys),m(e.teams);let s=[...e.teams];s.length>0?(console.log("response['teams']: ".concat(s)),N(s[0])):N(S),sessionStorage.setItem("userData"+l,JSON.stringify(e.keys)),sessionStorage.setItem("userSpendData"+l,JSON.stringify(e.user_info));let a=(await v(g,l,t)).data.map(e=>e.id);console.log("available_model_names:",a),k(a),console.log("userModels:",_),sessionStorage.setItem("userModels"+l,JSON.stringify(a))}catch(e){console.error("There was an error fetching the data",e)}})()}},[l,j,g,a,t]),(0,n.useEffect)(()=>{if(null!==a&&null!=A){let e=0;for(let l of a)A.hasOwnProperty("team_id")&&null!==l.team_id&&l.team_id===A.team_id&&(e+=l.spend);w(e)}else if(null!==a){let e=0;for(let l of a)e+=l.spend;w(e)}},[A]),null==l||null==j){let e="/sso/key/generate";return console.log("Full URL:",e),window.location.href=e,null}if(null==g)return null;if(null==t&&i("App Owner"),t&&"Admin Viewer"==t){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to create keys"})]})}return console.log("inside user dashboard, selected team",A),console.log("teamSpend: ".concat(y)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eF,{userID:l,userRole:t,selectedTeam:A||null,accessToken:g}),(0,r.jsx)(eE,{userID:l,userRole:t,accessToken:g,userSpend:y,selectedTeam:A||null}),(0,r.jsx)(eI,{userID:l,userRole:t,accessToken:g,selectedTeam:A||null,data:a,setData:h,teams:s}),(0,r.jsx)(em,{userID:l,team:A||null,userRole:t,accessToken:g,data:a,setData:h},A?A.team_id:null),(0,r.jsx)(eO,{teams:s,setSelectedTeam:N,userRole:t})]})})})},eU=t(92836),eD=t(26734),ez=t(41608),eB=t(32126),eq=t(23682),eK=t(47047),eV=t(76628),eG=t(38302),eW=t(28683),eJ=t(1460),eY=t(78578),eH=t(63954),e$=e=>{let{modelID:l,accessToken:t}=e,[s,a]=(0,n.useState)(!1),o=async()=>{try{h.ZP.info("Making API Call"),a(!0);let e=await p(t,l);console.log("model delete Response:",e),h.ZP.success("Model ".concat(l," deleted successfully")),a(!1)}catch(e){console.error("Error deleting the model:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(eZ.Z,{onClick:()=>a(!0),icon:ex.Z,size:"sm"}),(0,r.jsx)(en.Z,{open:s,onOk:o,okType:"danger",onCancel:()=>a(!1),children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 w-full",children:[(0,r.jsx)(et.Z,{children:"Delete Model"}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)("p",{children:"Are you sure you want to delete this model? This action is irreversible."})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("p",{children:["Model ID: ",(0,r.jsx)("b",{children:l})]})})]})})]})},eX=t(97766),eQ=t(46495);let{Title:e0,Link:e1}=eR.default;(s=a||(a={})).OpenAI="OpenAI",s.Azure="Azure",s.Anthropic="Anthropic",s.Google_AI_Studio="Gemini (Google AI Studio)",s.Bedrock="Amazon Bedrock",s.OpenAI_Compatible="OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)",s.Vertex_AI="Vertex AI (Anthropic, Gemini, etc.)";let e2={OpenAI:"openai",Azure:"azure",Anthropic:"anthropic",Google_AI_Studio:"gemini",Bedrock:"bedrock",OpenAI_Compatible:"openai",Vertex_AI:"vertex_ai"},e4=async(e,l,t)=>{try{let s=Array.isArray(e.model)?e.model:[e.model];console.log("received deployments: ".concat(s)),console.log("received type of deployments: ".concat(typeof s)),s.forEach(async t=>{console.log("litellm_model: ".concat(t));let s={},a={};s.model=t;let r="";for(let[l,t]of Object.entries(e))if(""!==t){if("model_name"==l)r+=t;else if("custom_llm_provider"==l)continue;else if("model"==l)continue;else if("base_model"===l)a[l]=t;else if("litellm_extra_params"==l){console.log("litellm_extra_params:",t);let e={};if(t&&void 0!=t){try{e=JSON.parse(t)}catch(e){throw h.ZP.error("Failed to parse LiteLLM Extra Params: "+e,20),Error("Failed to parse litellm_extra_params: "+e)}for(let[l,t]of Object.entries(e))s[l]=t}}else s[l]=t}let n={model_name:r,litellm_params:s,model_info:a},o=await x(l,n);console.log("response for model create call: ".concat(o.data))}),t.resetFields()}catch(e){h.ZP.error("Failed to create model: "+e,20)}};var e8=e=>{var l,t,s;let{accessToken:o,token:i,userRole:c,userID:d,modelData:m={data:[]},setModelData:x}=e,[p,j]=(0,n.useState)([]),[g]=er.Z.useForm(),[f,y]=(0,n.useState)(null),[Z,w]=(0,n.useState)(""),[b,k]=(0,n.useState)([]),v=Object.values(a).filter(e=>isNaN(Number(e))),[S,A]=(0,n.useState)("OpenAI"),[N,C]=(0,n.useState)(""),[I,P]=(0,n.useState)(!1),[T,E]=(0,n.useState)(null),[F,M]=(0,n.useState)([]),[R,L]=(0,n.useState)(null),U=e=>{E(e),P(!0)},z=async e=>{if(console.log("handleEditSubmit:",e),null==o)return;let l={},t=null;for(let[s,a]of Object.entries(e))"model_id"!==s?l[s]=a:t=a;let s={litellm_params:l,model_info:{id:t}};console.log("handleEditSubmit payload:",s),await D(o,s),h.ZP.success("Model updated successfully, restart server to see updates"),P(!1),E(null)},B=()=>{w(new Date().toLocaleString())};if((0,n.useEffect)(()=>{if(!o||!i||!c||!d)return;let e=async()=>{try{let e=await _(o,d,c);console.log("Model data response:",e.data),x(e);let l=new Set;for(let t=0;t{let e=await u();console.log("received model cost map data: ".concat(Object.keys(e))),y(e)};null==f&&l(),B()},[o,i,c,d,f,Z]),!m||!o||!i||!c||!d)return(0,r.jsx)("div",{children:"Loading..."});let q=[];for(let e=0;e(console.log("GET PROVIDER CALLED! - ".concat(f)),null!=f&&"object"==typeof f&&e in f)?f[e].litellm_provider:"openai";if(a){let e=a.split("/"),l=e[0];n=1===e.length?h(a):l}else n="openai";r&&(o=null==r?void 0:r.input_cost_per_token,i=null==r?void 0:r.output_cost_per_token,c=null==r?void 0:r.max_tokens),(null==s?void 0:s.litellm_params)&&(d=Object.fromEntries(Object.entries(null==s?void 0:s.litellm_params).filter(e=>{let[l]=e;return"model"!==l&&"api_base"!==l}))),m.data[e].provider=n,m.data[e].input_cost=o,m.data[e].output_cost=i,m.data[e].max_tokens=c,m.data[e].api_base=null==s?void 0:null===(t=s.litellm_params)||void 0===t?void 0:t.api_base,m.data[e].cleanedLitellmParams=d,q.push(s.model_name),console.log(m.data[e])}if(c&&"Admin Viewer"==c){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to view all models"})]})}let K=e=>{console.log("received provider string: ".concat(e));let l=Object.keys(a).find(l=>a[l]===e);if(l){let e=e2[l];console.log("mappingResult: ".concat(e));let t=[];"object"==typeof f&&Object.entries(f).forEach(l=>{let[s,a]=l;null!==a&&"object"==typeof a&&"litellm_provider"in a&&(a.litellm_provider===e||a.litellm_provider.includes(e))&&t.push(s)}),k(t),console.log("providerModels: ".concat(b))}},V=async()=>{try{h.ZP.info("Running health check..."),C("");let e=await W(o);C(e)}catch(e){console.error("Error running health check:",e),C("Error running health check")}};return console.log("selectedProvider: ".concat(S)),console.log("providerModels.length: ".concat(b.length)),(0,r.jsx)("div",{style:{width:"100%",height:"100%"},children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{className:"flex justify-between mt-2 w-full items-center",children:[(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)(eU.Z,{children:"All Models"}),(0,r.jsx)(eU.Z,{children:"Add Model"}),(0,r.jsx)(eU.Z,{children:(0,r.jsx)("pre",{children:"/health Models"})})]}),(0,r.jsxs)("div",{className:"flex items-center space-x-2",children:[Z&&(0,r.jsxs)(el.Z,{children:["Last Refreshed: ",Z]}),(0,r.jsx)(eZ.Z,{icon:eH.Z,variant:"shadow",size:"xs",className:"self-center",onClick:B})]})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(Y.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)(el.Z,{children:"Filter by Public Model Name"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2 ml-2 w-50",defaultValue:"all",onValueChange:e=>L("all"===e?"all":e),children:[(0,r.jsx)(eN.Z,{value:"all",children:"All Models"}),F.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>L(e),children:e},l))]})]}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Public Model Name "}),(0,r.jsx)(ev.Z,{children:"Provider"}),"Admin"===c&&(0,r.jsx)(ev.Z,{children:"API Base"}),(0,r.jsx)(ev.Z,{children:"Extra litellm Params"}),(0,r.jsx)(ev.Z,{children:"Input Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Output Price per token ($)"}),(0,r.jsx)(ev.Z,{children:"Max Tokens"})]})}),(0,r.jsx)(eb.Z,{children:m.data.filter(e=>"all"===R||e.model_name===R||null==R||""===R).map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:e.model_name})}),(0,r.jsx)(e_.Z,{children:e.provider}),"Admin"===c&&(0,r.jsx)(e_.Z,{children:e.api_base}),(0,r.jsx)(e_.Z,{children:(0,r.jsxs)(X.Z,{children:[(0,r.jsx)(ee.Z,{children:(0,r.jsx)(el.Z,{children:"Litellm params"})}),(0,r.jsx)(Q.Z,{children:(0,r.jsx)("pre",{children:JSON.stringify(e.cleanedLitellmParams,null,2)})})]})}),(0,r.jsx)(e_.Z,{children:e.input_cost}),(0,r.jsx)(e_.Z,{children:e.output_cost}),(0,r.jsx)(e_.Z,{children:e.max_tokens}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>U(e)}),(0,r.jsx)(e$,{modelID:e.model_info.id,accessToken:o})]})]},l))})]})})]}),(0,r.jsx)(e=>{let{visible:l,onCancel:t,model:s,onSubmit:a}=e,[n]=er.Z.useForm(),o={},i="",c="";if(s){o=s.litellm_params,i=s.model_name;let e=s.model_info;e&&(c=e.id,console.log("model_id: ".concat(c)),o.model_id=c)}return(0,r.jsx)(en.Z,{title:"Edit Model "+i,visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a(e),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:z,initialValues:o,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{className:"mt-8",label:"api_base",name:"api_base",children:(0,r.jsx)($.Z,{})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.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,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"max_retries",name:"max_retries",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"timeout",name:"timeout",tooltip:"int (optional) - Timeout in seconds for LLM requests (Defaults to 600 seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"stream_timeout",name:"stream_timeout",tooltip:"int (optional) - Timeout for stream requests (seconds)",children:(0,r.jsx)(ei.Z,{min:0,step:1})}),(0,r.jsx)(er.Z.Item,{label:"model_id",name:"model_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})},{visible:I,onCancel:()=>{P(!1),E(null)},model:T,onSubmit:z})]}),(0,r.jsxs)(eB.Z,{className:"h-full",children:[(0,r.jsx)(e0,{level:2,children:"Add new model"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(er.Z,{form:g,onFinish:()=>{g.validateFields().then(e=>{e4(e,o,g)}).catch(e=>{console.error("Validation failed:",e)})},labelCol:{span:10},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.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,r.jsx)(eA.Z,{value:S.toString(),children:v.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>{K(e),A(e)},children:e},l))})}),(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"Vertex AI (Anthropic, Gemini, etc.)"===(s=S.toString())?"gemini-pro":"Anthropic"==s?"claude-3-opus":"Amazon Bedrock"==s?"claude-3-opus":"Gemini (Google AI Studio)"==s?"gemini-pro":"gpt-3.5-turbo"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Model name your users will pass in."})})]}),(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"LiteLLM Model Name(s)",name:"model",tooltip:"Actual model name used for making litellm.completion() call.",className:"mb-0",children:"Azure"===S?(0,r.jsx)($.Z,{placeholder:"Enter model name"}):b.length>0?(0,r.jsx)(eK.Z,{value:b,children:b.map((e,l)=>(0,r.jsx)(eV.Z,{value:e,children:e},l))}):(0,r.jsx)($.Z,{placeholder:"gpt-3.5-turbo-0125"})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Actual model name used for making ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/providers",target:"_blank",children:"litellm.completion() call"}),". We'll ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/proxy/reliability#step-1---set-deployments-on-config",target:"_blank",children:"loadbalance"})," models with the same 'public name'"]})})]}),"Amazon Bedrock"!=S&&"Vertex AI (Anthropic, Gemini, etc.)"!=S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Key",name:"api_key",children:(0,r.jsx)($.Z,{placeholder:"sk-",type:"password"})}),"OpenAI"==S&&(0,r.jsx)(er.Z.Item,{label:"Organization ID",name:"organization_id",children:(0,r.jsx)($.Z,{placeholder:"[OPTIONAL] my-unique-org"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Project",name:"vertex_project",children:(0,r.jsx)($.Z,{placeholder:"adroit-cadet-1234.."})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Location",name:"vertex_location",children:(0,r.jsx)($.Z,{placeholder:"us-east-1"})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"Vertex Credentials",name:"vertex_credentials",className:"mb-0",children:(0,r.jsx)(eQ.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;g.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?h.ZP.success("".concat(e.file.name," file uploaded successfully")):"error"===e.file.status&&h.ZP.error("".concat(e.file.name," file upload failed."))},children:(0,r.jsx)(ec.ZP,{icon:(0,r.jsx)(eX.Z,{}),children:"Click to Upload"})})}),"Vertex AI (Anthropic, Gemini, etc.)"==S&&(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsx)(el.Z,{className:"mb-3 mt-1",children:"Give litellm a gcp service account(.json file), so it can make the relevant calls"})})]}),("Azure"==S||"OpenAI-Compatible Endpoints (Groq, Together AI, Mistral AI, etc.)"==S)&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Base",name:"api_base",children:(0,r.jsx)($.Z,{placeholder:"https://..."})}),"Azure"==S&&(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"API Version",name:"api_version",children:(0,r.jsx)($.Z,{placeholder:"2023-07-01-preview"})}),"Azure"==S&&(0,r.jsxs)(er.Z.Item,{label:"Base Model",name:"base_model",children:[(0,r.jsx)($.Z,{placeholder:"azure/gpt-3.5-turbo"}),(0,r.jsxs)(el.Z,{children:["The actual model your azure deployment uses. Used for accurate cost tracking. Select name from ",(0,r.jsx)(e1,{href:"https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",target:"_blank",children:"here"})]})]}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:""})}),"Amazon Bedrock"==S&&(0,r.jsx)(er.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,r.jsx)($.Z,{placeholder:"us-east-1"})}),(0,r.jsx)(er.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,r.jsx)(eY.Z,{rows:4,placeholder:'{ "rpm": 100, "timeout": 0, "stream_timeout": 0 }'})}),(0,r.jsxs)(eG.Z,{children:[(0,r.jsx)(eW.Z,{span:10}),(0,r.jsx)(eW.Z,{span:10,children:(0,r.jsxs)(el.Z,{className:"mb-3 mt-1",children:["Pass JSON of litellm supported params ",(0,r.jsx)(e1,{href:"https://docs.litellm.ai/docs/completion/input",target:"_blank",children:"litellm.completion() call"})]})})]})]}),(0,r.jsx)("div",{style:{textAlign:"center",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Model"})}),(0,r.jsx)(eJ.Z,{title:"Get help on our github",children:(0,r.jsx)(eR.default.Link,{href:"https://github.com/BerriAI/litellm/issues",children:"Need Help?"})})]})})]}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(el.Z,{children:"`/health` will run a very small request through your models configured on litellm"}),(0,r.jsx)(H.Z,{onClick:V,children:"Run `/health`"}),N&&(0,r.jsx)("pre",{children:JSON.stringify(N,null,2)})]})})]})]})})};let{Option:e5}=ea.default;var e3=e=>{let{userID:l,accessToken:t,teams:s}=e,[a]=er.Z.useForm(),[o,i]=(0,n.useState)(!1),[c,d]=(0,n.useState)(null),[m,u]=(0,n.useState)([]);(0,n.useEffect)(()=>{(async()=>{try{let e=await v(t,l,"any"),s=[];for(let l=0;l{i(!1),a.resetFields()},p=()=>{i(!1),d(null),a.resetFields()},j=async e=>{try{h.ZP.info("Making API Call"),i(!0),console.log("formValues in create user:",e);let s=await g(t,null,e);console.log("user create Response:",s),d(s.key),h.ZP.success("API user Created"),a.resetFields(),localStorage.removeItem("userData"+l)}catch(e){console.error("Error creating the user:",e)}};return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>i(!0),children:"+ Invite User"}),(0,r.jsxs)(en.Z,{title:"Invite User",visible:o,width:800,footer:null,onOk:x,onCancel:p,children:[(0,r.jsx)(el.Z,{className:"mb-1",children:"Invite a user to login to the Admin UI and create Keys"}),(0,r.jsx)(el.Z,{className:"mb-6",children:(0,r.jsx)("b",{children:"Note: SSO Setup Required for this"})}),(0,r.jsxs)(er.Z,{form:a,onFinish:j,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsx)(er.Z.Item,{label:"User Email",name:"user_email",children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"team_id",children:(0,r.jsx)(ea.default,{placeholder:"Select Team ID",style:{width:"100%"},children:s?s.map(e=>(0,r.jsx)(e5,{value:e.team_id,children:e.team_alias},e.team_id)):(0,r.jsx)(e5,{value:null,children:"Default Team"},"default")})}),(0,r.jsx)(er.Z.Item,{label:"Metadata",name:"metadata",children:(0,r.jsx)(eo.Z.TextArea,{rows:4,placeholder:"Enter metadata as JSON"})}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create User"})})]})]}),c&&(0,r.jsxs)(en.Z,{title:"User Created Successfully",visible:o,onOk:x,onCancel:p,footer:null,children:[(0,r.jsx)("p",{children:"User has been created to access your proxy. Please Ask them to Log In."}),(0,r.jsx)("br",{}),(0,r.jsx)("p",{children:(0,r.jsx)("b",{children:"Note: This Feature is only supported through SSO on the Admin UI"})})]})]})},e6=e=>{let{accessToken:l,token:t,keys:s,userRole:a,userID:o,teams:i,setKeys:c}=e,[d,m]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(0),[j,g]=n.useState(null),[f,y]=(0,n.useState)(null);if((0,n.useEffect)(()=>{if(!l||!t||!a||!o)return;let e=async()=>{try{let e=await Z(l,null,a,!0,x,25);console.log("user data response:",e),m(e)}catch(e){console.error("There was an error fetching the model data",e)}};l&&t&&a&&o&&e();let s=async()=>{try{let e=await T(l,null);console.log("user data response:",e),u(e)}catch(e){console.error("There was an error fetching the model data",e)}};a&&("Admin"==a||"Admin Viewer"==a)&&!h&&s()},[l,t,a,o,x]),!d||!l||!t||!a||!o)return(0,r.jsx)("div",{children:"Loading..."});let w=async e=>{try{let t=await T(l,e);console.log("user data response:",t),u(t)}catch(e){console.error("There was an error fetching the model data",e)}};return(0,r.jsx)("div",{style:{width:"100%"},children:(0,r.jsxs)(Y.Z,{className:"gap-2 p-2 h-[80vh] w-full mt-8",children:[(0,r.jsx)(e3,{userID:o,accessToken:l,teams:i}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[80vh] mb-4",children:[(0,r.jsxs)("div",{className:"mb-4 mt-1",children:[(0,r.jsxs)(el.Z,{children:[(0,r.jsx)("b",{children:"Key Owners: "})," Users on LiteLLM that created API Keys. Automatically tracked by LiteLLM"]}),(0,r.jsxs)(el.Z,{className:"mt-1",children:[(0,r.jsx)("b",{children:"End Users: "}),"End Users of your LLM API calls. Tracked When a `user` param is passed in your LLM calls"]})]}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Key Owners"}),(0,r.jsx)(eU.Z,{value:"2",children:"End-Users"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(ew.Z,{className:"mt-5",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"User ID"}),(0,r.jsx)(ev.Z,{children:"User Email"}),(0,r.jsx)(ev.Z,{children:"User Models"}),(0,r.jsx)(ev.Z,{children:"User Spend ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User Max Budget ($ USD)"}),(0,r.jsx)(ev.Z,{children:"User API Key Aliases"})]})}),(0,r.jsx)(eb.Z,{children:d.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_id}),(0,r.jsx)(e_.Z,{children:e.user_email}),(0,r.jsx)(e_.Z,{children:e.models&&e.models.length>0?e.models:"All Models"}),(0,r.jsx)(e_.Z,{children:e.spend?e.spend:0}),(0,r.jsx)(e_.Z,{children:e.max_budget?e.max_budget:"Unlimited"}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(Y.Z,{numItems:2,children:e&&e.key_aliases&&e.key_aliases.filter(e=>null!==e).length>0?(0,r.jsx)(ep.Z,{size:"xs",color:"indigo",children:e.key_aliases.filter(e=>null!==e).join(", ")}):(0,r.jsx)(ep.Z,{size:"xs",color:"gray",children:"No Keys"})})})]},e.user_id))})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)("div",{className:"flex items-center",children:[(0,r.jsx)("div",{className:"flex-1"}),(0,r.jsxs)("div",{className:"flex-1 flex justify-between items-center",children:[(0,r.jsx)(el.Z,{className:"w-1/4 mr-2 text-right",children:"Key"}),(0,r.jsx)(eA.Z,{defaultValue:"1",className:"w-3/4",children:null==s?void 0:s.map((e,l)=>{if(e&&null!==e.key_alias&&e.key_alias.length>0)return(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>w(e.token),children:e.key_alias},l)})})]})]}),(0,r.jsxs)(ew.Z,{className:"max-h-[70vh] min-h-[500px]",children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"End User"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Total Events"})]})}),(0,r.jsx)(eb.Z,{children:null==h?void 0:h.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.end_user}),(0,r.jsx)(e_.Z,{children:e.total_spend}),(0,r.jsx)(e_.Z,{children:e.total_events})]},l))})]})]})]})]})]}),function(){if(!d)return null;let e=Math.ceil(d.length/25);return(0,r.jsxs)("div",{className:"flex justify-between items-center",children:[(0,r.jsxs)("div",{children:["Showing Page ",x+1," of ",e]}),(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-l focus:outline-none",disabled:0===x,onClick:()=>p(x-1),children:"← Prev"}),(0,r.jsx)("button",{className:"bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-r focus:outline-none",onClick:()=>{p(x+1)},children:"Next →"})]})]})}()]})})},e7=e=>{let{teams:l,searchParams:t,accessToken:s,setTeams:a,userID:o,userRole:i}=e,[c]=er.Z.useForm(),[d]=er.Z.useForm(),{Title:m,Paragraph:u}=eR.default,[x,p]=(0,n.useState)(""),[j,g]=(0,n.useState)(!1),[f,Z]=(0,n.useState)(l?l[0]:null),[b,_]=(0,n.useState)(!1),[k,S]=(0,n.useState)(!1),[A,N]=(0,n.useState)([]),[C,I]=(0,n.useState)(!1),[P,T]=(0,n.useState)(null),[E,F]=(0,n.useState)({}),O=e=>{Z(e),g(!0)},M=async e=>{let t=e.team_id;if(console.log("handleEditSubmit:",e),null==s)return;let r=await U(s,e);l&&a(l.map(e=>e.team_id===t?r.data:e)),h.ZP.success("Team updated successfully"),g(!1),Z(null)},L=async e=>{T(e),I(!0)},D=async()=>{if(null!=P&&null!=l&&null!=s){try{await y(s,P);let e=l.filter(e=>e.team_id!==P);a(e)}catch(e){console.error("Error deleting the team:",e)}I(!1),T(null)}};(0,n.useEffect)(()=>{let e=async()=>{try{if(null===o||null===i||null===s||null===l)return;console.log("fetching team info:");let e={};for(let t=0;t<(null==l?void 0:l.length);t++){let a=l[t].team_id,r=await w(s,a);console.log("teamInfo response:",r),null!==r&&(e={...e,[a]:r})}F(e)}catch(e){console.error("Error fetching team info:",e)}};(async()=>{try{if(null===o||null===i)return;if(null!==s){let e=(await v(s,o,i)).data.map(e=>e.id);console.log("available_model_names:",e),N(e)}}catch(e){console.error("Error fetching user models:",e)}})(),e()},[s,o,i,l]);let B=async e=>{try{if(null!=s){h.ZP.info("Creating Team");let t=await R(s,e);null!==l?a([...l,t]):a([t]),console.log("response for team create call: ".concat(t)),h.ZP.success("Team created"),_(!1)}}catch(e){console.error("Error creating the team:",e),h.ZP.error("Error creating the team: "+e,20)}},q=async e=>{try{if(null!=s&&null!=l){h.ZP.info("Adding Member");let t={role:"user",user_email:e.user_email,user_id:e.user_id},r=await z(s,f.team_id,t);console.log("response for team create call: ".concat(r.data));let n=l.findIndex(e=>(console.log("team.team_id=".concat(e.team_id,"; response.data.team_id=").concat(r.data.team_id)),e.team_id===r.data.team_id));if(console.log("foundIndex: ".concat(n)),-1!==n){let e=[...l];e[n]=r.data,a(e),Z(r.data)}S(!1)}}catch(e){console.error("Error creating the team:",e)}};return console.log("received teams ".concat(l)),(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"All Teams"}),(0,r.jsxs)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Team Name"}),(0,r.jsx)(ev.Z,{children:"Spend (USD)"}),(0,r.jsx)(ev.Z,{children:"Budget (USD)"}),(0,r.jsx)(ev.Z,{children:"Models"}),(0,r.jsx)(ev.Z,{children:"TPM / RPM Limits"}),(0,r.jsx)(ev.Z,{children:"Info"})]})}),(0,r.jsx)(eb.Z,{children:l&&l.length>0?l.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.team_alias}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.spend}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:e.max_budget?e.max_budget:"No limit"}),(0,r.jsx)(e_.Z,{style:{maxWidth:"8-x",whiteSpace:"pre-wrap",overflow:"hidden"},children:Array.isArray(e.models)?(0,r.jsx)("div",{style:{display:"flex",flexDirection:"column"},children:0===e.models.length?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})}):e.models.map((e,l)=>"all-proxy-models"===e?(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"red",children:(0,r.jsx)(el.Z,{children:"All Proxy Models"})},l):(0,r.jsx)(ep.Z,{size:"xs",className:"mb-1",color:"blue",children:(0,r.jsx)(el.Z,{children:e.length>30?"".concat(e.slice(0,30),"..."):e})},l))}):null}),(0,r.jsx)(e_.Z,{style:{maxWidth:"4px",whiteSpace:"pre-wrap",overflow:"hidden"},children:(0,r.jsxs)(el.Z,{children:["TPM:"," ",e.tpm_limit?e.tpm_limit:"Unlimited"," ",(0,r.jsx)("br",{}),"RPM:"," ",e.rpm_limit?e.rpm_limit:"Unlimited"]})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].keys&&E[e.team_id].keys.length," Keys"]}),(0,r.jsxs)(el.Z,{children:[E&&e.team_id&&E[e.team_id]&&E[e.team_id].team_info&&E[e.team_id].team_info.members_with_roles&&E[e.team_id].team_info.members_with_roles.length," Members"]})]}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>O(e)}),(0,r.jsx)(eZ.Z,{onClick:()=>L(e.team_id),icon:ex.Z,size:"sm"})]})]},e.team_id)):null})]}),C&&(0,r.jsx)("div",{className:"fixed z-10 inset-0 overflow-y-auto",children:(0,r.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,r.jsx)("div",{className:"fixed inset-0 transition-opacity","aria-hidden":"true",children:(0,r.jsx)("div",{className:"absolute inset-0 bg-gray-500 opacity-75"})}),(0,r.jsx)("span",{className:"hidden sm:inline-block sm:align-middle sm:h-screen","aria-hidden":"true",children:"​"}),(0,r.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,r.jsx)("div",{className:"bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4",children:(0,r.jsx)("div",{className:"sm:flex sm:items-start",children:(0,r.jsxs)("div",{className:"mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left",children:[(0,r.jsx)("h3",{className:"text-lg leading-6 font-medium text-gray-900",children:"Delete Team"}),(0,r.jsx)("div",{className:"mt-2",children:(0,r.jsx)("p",{className:"text-sm text-gray-500",children:"Are you sure you want to delete this team ?"})})]})})}),(0,r.jsxs)("div",{className:"bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse",children:[(0,r.jsx)(H.Z,{onClick:D,color:"red",className:"ml-2",children:"Delete"}),(0,r.jsx)(H.Z,{onClick:()=>{I(!1),T(null)},children:"Cancel"})]})]})]})})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>_(!0),children:"+ Create New Team"}),(0,r.jsx)(en.Z,{title:"Create Team",visible:b,width:800,footer:null,onOk:()=>{_(!1),c.resetFields()},onCancel:()=>{_(!1),c.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:B,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)($.Z,{placeholder:""})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Create Team"})})]})})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(m,{level:4,children:"Team Members"}),(0,r.jsx)(u,{children:"If you belong to multiple teams, this setting controls which teams members you see."}),l&&l.length>0?(0,r.jsx)(eA.Z,{defaultValue:"0",children:l.map((e,l)=>(0,r.jsx)(eN.Z,{value:String(l),onClick:()=>{Z(e)},children:e.team_alias},l))}):(0,r.jsxs)(u,{children:["No team created. ",(0,r.jsx)("b",{children:"Defaulting to personal account."})]})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:f?f.members_with_roles.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.role})]},l)):null})]})}),f&&(0,r.jsx)(e=>{let{visible:l,onCancel:t,team:s,onSubmit:a}=e,[n]=er.Z.useForm();return(0,r.jsx)(en.Z,{title:"Edit Team",visible:l,width:800,footer:null,onOk:()=>{n.validateFields().then(e=>{a({...e,team_id:s.team_id}),n.resetFields()}).catch(e=>{console.error("Validation failed:",e)})},onCancel:t,children:(0,r.jsxs)(er.Z,{form:n,onFinish:M,initialValues:s,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Team Name",name:"team_alias",rules:[{required:!0,message:"Please input a team name"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"Models",name:"models",children:(0,r.jsxs)(ea.default,{mode:"multiple",placeholder:"Select models",style:{width:"100%"},children:[(0,r.jsx)(ea.default.Option,{value:"all-proxy-models",children:"All Proxy Models"},"all-proxy-models"),A&&A.map(e=>(0,r.jsx)(ea.default.Option,{value:e,children:e},e))]})}),(0,r.jsx)(er.Z.Item,{label:"Max Budget (USD)",name:"max_budget",children:(0,r.jsx)(ei.Z,{step:.01,precision:2,width:200})}),(0,r.jsx)(er.Z.Item,{label:"Tokens per minute Limit (TPM)",name:"tpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"rpm_limit",children:(0,r.jsx)(ei.Z,{step:1,width:400})}),(0,r.jsx)(er.Z.Item,{label:"Requests per minute Limit (RPM)",name:"team_id",hidden:!0})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Edit Team"})})]})})},{visible:j,onCancel:()=>{g(!1),Z(null)},team:f,onSubmit:M})]}),(0,r.jsxs)(J.Z,{numColSpan:1,children:[(0,r.jsx)(H.Z,{className:"mx-auto mb-5",onClick:()=>S(!0),children:"+ Add member"}),(0,r.jsx)(en.Z,{title:"Add member",visible:k,width:800,footer:null,onOk:()=>{S(!1),d.resetFields()},onCancel:()=>{S(!1),d.resetFields()},children:(0,r.jsxs)(er.Z,{form:c,onFinish:q,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]})})]})]})})},e9=t(18190),le=e=>{let l,{searchParams:t,accessToken:s,showSSOBanner:a}=e,[o]=er.Z.useForm(),[i]=er.Z.useForm(),{Title:c,Paragraph:d}=eR.default,[m,u]=(0,n.useState)(""),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)(!1),[f,y]=(0,n.useState)(!1),[Z,w]=(0,n.useState)(!1),[b,_]=(0,n.useState)(!1),[k,v]=(0,n.useState)(!1);try{l=window.location.origin}catch(e){l=""}l+="/fallback/login";let S=()=>{v(!1)},A=["proxy_admin","proxy_admin_viewer"];(0,n.useEffect)(()=>{(async()=>{if(null!=s){let e=[],l=await M(s,"proxy_admin_viewer");l.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy viewers: ".concat(l));let t=await M(s,"proxy_admin");t.forEach(l=>{e.push({user_role:l.user_role,user_id:l.user_id,user_email:l.user_email})}),console.log("proxy admins: ".concat(t)),console.log("combinedList: ".concat(e)),p(e)}})()},[s]);let N=()=>{w(!1),i.resetFields()},C=()=>{w(!1),i.resetFields()},I=e=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Email",name:"user_email",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_email",className:"px-3 py-2 border rounded-md w-full"})}),(0,r.jsx)("div",{className:"text-center mb-4",children:"OR"}),(0,r.jsx)(er.Z.Item,{label:"User ID",name:"user_id",className:"mb-4",children:(0,r.jsx)(eo.Z,{name:"user_id",className:"px-3 py-2 border rounded-md w-full"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add member"})})]}),P=(e,l,t)=>(0,r.jsxs)(er.Z,{form:o,onFinish:e,labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{rules:[{required:!0,message:"Required"}],label:"User Role",name:"user_role",labelCol:{span:10},labelAlign:"left",children:(0,r.jsx)(eA.Z,{value:l,children:A.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Team ID",name:"user_id",hidden:!0,initialValue:t,valuePropName:"user_id",className:"mt-8",children:(0,r.jsx)(eo.Z,{value:t,disabled:!0})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Update role"})})]}),T=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,null);console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),h.ZP.success("Refresh tab to see updated user role"),w(!1)}}catch(e){console.error("Error creating the key:",e)}},E=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call");let l=await B(s,e,"proxy_admin_viewer");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),g(!1)}}catch(e){console.error("Error creating the key:",e)}},F=async e=>{try{if(null!=s&&null!=x){h.ZP.info("Making API Call"),e.user_email,e.user_id;let l=await B(s,e,"proxy_admin");console.log("response for team create call: ".concat(l));let t=x.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(t)),-1==t&&(console.log("updates admin with new user"),x.push(l),p(x)),y(!1)}}catch(e){console.error("Error creating the key:",e)}},O=async e=>{null!=s&&G(s,{environment_variables:{PROXY_BASE_URL:e.proxy_base_url,GOOGLE_CLIENT_ID:e.google_client_id,GOOGLE_CLIENT_SECRET:e.google_client_secret}})};return console.log("admins: ".concat(null==x?void 0:x.length)),(0,r.jsxs)("div",{className:"w-full m-2 mt-2 p-8",children:[(0,r.jsx)(c,{level:4,children:"Admin Access "}),(0,r.jsxs)(d,{children:[a&&(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/ui#restrict-ui-access",children:"Requires SSO Setup"}),(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin: "})," Can create keys, teams, users, add models, etc. ",(0,r.jsx)("br",{}),(0,r.jsx)("b",{children:"Proxy Admin Viewer: "}),"Can just view spend. They cannot create keys, teams or grant users access to new models."," "]}),(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-2 w-full",children:[(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsx)(eg.Z,{className:"w-full mx-auto flex-auto overflow-y-auto max-h-[50vh]",children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Member Name"}),(0,r.jsx)(ev.Z,{children:"Role"})]})}),(0,r.jsx)(eb.Z,{children:x?x.map((e,l)=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.user_email?e.user_email:e.user_id?e.user_id:null}),(0,r.jsx)(e_.Z,{children:e.user_role}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(eZ.Z,{icon:eu.Z,size:"sm",onClick:()=>w(!0)}),(0,r.jsx)(en.Z,{title:"Update role",visible:Z,width:800,footer:null,onOk:N,onCancel:C,children:P(T,e.user_role,e.user_id)})]})]},l)):null})]})})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)("div",{className:"flex justify-start",children:[(0,r.jsx)(H.Z,{className:"mr-4 mb-5",onClick:()=>y(!0),children:"+ Add admin"}),(0,r.jsx)(en.Z,{title:"Add admin",visible:f,width:800,footer:null,onOk:()=>{y(!1),i.resetFields()},onCancel:()=>{y(!1),i.resetFields()},children:I(F)}),(0,r.jsx)(H.Z,{className:"mb-5",onClick:()=>g(!0),children:"+ Add viewer"}),(0,r.jsx)(en.Z,{title:"Add viewer",visible:j,width:800,footer:null,onOk:()=>{g(!1),i.resetFields()},onCancel:()=>{g(!1),i.resetFields()},children:I(E)})]})})]}),(0,r.jsxs)(Y.Z,{children:[(0,r.jsx)(c,{level:4,children:"Add SSO"}),(0,r.jsxs)("div",{className:"flex justify-start mb-4",children:[(0,r.jsx)(H.Z,{onClick:()=>_(!0),children:"Add SSO"}),(0,r.jsx)(en.Z,{title:"Add SSO",visible:b,width:800,footer:null,onOk:()=>{_(!1),o.resetFields()},onCancel:()=>{_(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{F(e),O(e),_(!1),v(!0)},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Admin Email",name:"user_email",rules:[{required:!0,message:"Please enter the email of the proxy admin"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"PROXY BASE URL",name:"proxy_base_url",rules:[{required:!0,message:"Please enter the proxy base url"}],children:(0,r.jsx)(eo.Z,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT ID",name:"google_client_id",rules:[{required:!0,message:"Please enter the google client id"}],children:(0,r.jsx)(eo.Z.Password,{})}),(0,r.jsx)(er.Z.Item,{label:"GOOGLE CLIENT SECRET",name:"google_client_secret",rules:[{required:!0,message:"Please enter the google client secret"}],children:(0,r.jsx)(eo.Z.Password,{})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})}),(0,r.jsxs)(en.Z,{title:"SSO Setup Instructions",visible:k,width:800,footer:null,onOk:S,onCancel:()=>{v(!1)},children:[(0,r.jsx)("p",{children:"Follow these steps to complete the SSO setup:"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"1. DO NOT Exit this TAB"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"2. Open a new tab, visit your proxy base url"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"3. Confirm your SSO is configured correctly and you can login on the new Tab"}),(0,r.jsx)(el.Z,{className:"mt-2",children:"4. If Step 3 is successful, you can close this tab"}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{onClick:S,children:"Done"})})]})]}),(0,r.jsxs)(e9.Z,{title:"Login without SSO",color:"teal",children:["If you need to login without sso, you can access ",(0,r.jsxs)("a",{href:l,target:"_blank",children:[(0,r.jsx)("b",{children:l})," "]})]})]})]})},ll=t(12224),lt=e=>{let{accessToken:l,userRole:t,userID:s}=e,[a,o]=(0,n.useState)([]),[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)(!1),[u]=er.Z.useForm(),[x,p]=(0,n.useState)(null),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)(""),[Z,w]=(0,n.useState)({}),[b,_]=(0,n.useState)([]),k=e=>{b.includes(e)?_(b.filter(l=>l!==e)):_([...b,e])},v={llm_exceptions:"LLM Exceptions",llm_too_slow:"LLM Responses Too Slow",llm_requests_hanging:"LLM Requests Hanging",budget_alerts:"Budget Alerts (API Keys, Users)"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),o(e.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 t=e.variables.SLACK_WEBHOOK_URL;console.log("catch_all_webhook",t),_(e.active_alerts),y(t),w(e.alerts_to_webhook)}c(l)})},[l,t,s]);let S=e=>b&&b.includes(e),A=e=>{if(!l)return;let t=Object.fromEntries(Object.entries(e.variables).map(e=>{var l;let[t,s]=e;return[t,(null===(l=document.querySelector('input[name="'.concat(t,'"]')))||void 0===l?void 0:l.value)||s]}));console.log("updatedVariables",t),console.log("updateAlertTypes",j);try{G(l,{environment_variables:t})}catch(e){h.ZP.error("Failed to update callback: "+e,20)}h.ZP.success("Callback updated successfully")},N=()=>{l&&u.validateFields().then(e=>{if(console.log("Form values:",e),"langfuse"===e.callback){G(l,{environment_variables:{LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey},litellm_settings:{success_callback:[e.callback]}});let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:null,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:e.langfusePublicKey,LANGFUSE_SECRET_KEY:e.langfusePrivateKey}};o(a?[...a,t]:[t])}else if("slack"===e.callback){console.log("values.slackWebhookUrl: ".concat(e.slackWebhookUrl)),G(l,{general_settings:{alerting:["slack"],alerting_threshold:300},environment_variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl}}),console.log("values.callback: ".concat(e.callback));let t={name:e.callback,variables:{SLACK_WEBHOOK_URL:e.slackWebhookUrl,LANGFUSE_HOST:null,LANGFUSE_PUBLIC_KEY:null,LANGFUSE_SECRET_KEY:null}};o(a?[...a,t]:[t])}m(!1),u.resetFields(),p(null)})};return l?(console.log("callbacks: ".concat(a)),(0,r.jsxs)("div",{className:"w-full mx-4",children:[(0,r.jsx)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"Logging Callbacks"}),(0,r.jsx)(eU.Z,{value:"2",children:"Alerting"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Callback"}),(0,r.jsx)(ev.Z,{children:"Callback Env Vars"})]})}),(0,r.jsx)(eb.Z,{children:a.map((e,t)=>{var s;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ep.Z,{color:"emerald",children:e.name})}),(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)("ul",{children:Object.entries(null!==(s=e.variables)&&void 0!==s?s:{}).filter(e=>{let[l,t]=e;return null!==t}).map(e=>{let[l,t]=e;return(0,r.jsxs)("li",{children:[(0,r.jsx)(el.Z,{className:"mt-2",children:l}),"LANGFUSE_HOST"===l?(0,r.jsx)("p",{children:"default value=https://cloud.langfuse.com"}):(0,r.jsx)("div",{}),(0,r.jsx)($.Z,{name:l,defaultValue:t,type:"password"})]},l)})}),(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>A(e),children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,e.name),className:"mx-2",children:"Test Callback"})]})]},t)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{console.log("Add callback clicked"),m(!0)},children:"Add Callback"})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsxs)(el.Z,{className:"my-2",children:["Alerts are only supported for Slack Webhook URLs. Get your webhook urls from ",(0,r.jsx)("a",{href:"https://api.slack.com/messaging/webhooks",target:"_blank",style:{color:"blue"},children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{}),(0,r.jsx)(ev.Z,{children:"Slack Webhook URL"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(v).map((e,l)=>{let[t,s]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:(0,r.jsx)(ll.Z,{id:"switch",name:"switch",checked:S(t),onChange:()=>k(t)})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(el.Z,{children:s})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:t,type:"password",defaultValue:Z&&Z[t]?Z[t]:f})})]},l)})})]}),(0,r.jsx)(H.Z,{size:"xs",className:"mt-2",onClick:()=>{if(!l)return;let e={};Object.entries(v).forEach(l=>{let[t,s]=l,a=document.querySelector('input[name="'.concat(t,'"]'));console.log("key",t),console.log("webhookInput",a);let r=(null==a?void 0:a.value)||"";console.log("newWebhookValue",r),e[t]=r}),console.log("updatedAlertToWebhooks",e);let t={general_settings:{alert_to_webhook_url:e,alert_types:b}};console.log("payload",t);try{G(l,t)}catch(e){h.ZP.error("Failed to update alerts: "+e,20)}h.ZP.success("Alerts updated successfully")},children:"Save Changes"}),(0,r.jsx)(H.Z,{onClick:()=>K(l,"slack"),className:"mx-2",children:"Test Alerts"})]})})]})]})}),(0,r.jsx)(en.Z,{title:"Add Callback",visible:d,onOk:N,width:800,onCancel:()=>{m(!1),u.resetFields(),p(null)},footer:null,children:(0,r.jsxs)(er.Z,{form:u,layout:"vertical",onFinish:N,children:[(0,r.jsx)(er.Z.Item,{label:"Callback",name:"callback",rules:[{required:!0,message:"Please select a callback"}],children:(0,r.jsx)(ea.default,{onChange:e=>{p(e)},children:(0,r.jsx)(ea.default.Option,{value:"langfuse",children:"langfuse"})})}),"langfuse"===x&&(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PUBLIC_KEY",name:"langfusePublicKey",rules:[{required:!0,message:"Please enter the public key"}],children:(0,r.jsx)($.Z,{type:"password"})}),(0,r.jsx)(er.Z.Item,{label:"LANGFUSE_PRIVATE_KEY",name:"langfusePrivateKey",rules:[{required:!0,message:"Please enter the private key"}],children:(0,r.jsx)($.Z,{type:"password"})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Save"})})]})})]})):null};let{Option:ls}=ea.default;var la=e=>{let{models:l,accessToken:t,routerSettings:s,setRouterSettings:a}=e,[o]=er.Z.useForm(),[i,c]=(0,n.useState)(!1),[d,m]=(0,n.useState)("");return(0,r.jsxs)("div",{children:[(0,r.jsx)(H.Z,{className:"mx-auto",onClick:()=>c(!0),children:"+ Add Fallbacks"}),(0,r.jsx)(en.Z,{title:"Add Fallbacks",visible:i,width:800,footer:null,onOk:()=>{c(!1),o.resetFields()},onCancel:()=>{c(!1),o.resetFields()},children:(0,r.jsxs)(er.Z,{form:o,onFinish:e=>{console.log(e);let{model_name:l,models:r}=e,n=[...s.fallbacks||[],{[l]:r}],i={...s,fallbacks:n};console.log(i);try{G(t,{router_settings:i}),a(i)}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully"),c(!1),o.resetFields()},labelCol:{span:8},wrapperCol:{span:16},labelAlign:"left",children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(er.Z.Item,{label:"Public Model Name",name:"model_name",rules:[{required:!0,message:"Set the model to fallback for"}],help:"required",children:(0,r.jsx)(eA.Z,{defaultValue:d,children:l&&l.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>m(e),children:e},l))})}),(0,r.jsx)(er.Z.Item,{label:"Fallback Models",name:"models",rules:[{required:!0,message:"Please select a model"}],help:"required",children:(0,r.jsx)(eK.Z,{value:l,children:l&&l.filter(e=>e!=d).map(e=>(0,r.jsx)(eV.Z,{value:e,children:e},e))})})]}),(0,r.jsx)("div",{style:{textAlign:"right",marginTop:"10px"},children:(0,r.jsx)(ec.ZP,{htmlType:"submit",children:"Add Fallbacks"})})]})})]})},lr=t(12968);async function ln(e,l){console.log("isLocal:",!1);let t=window.location.origin,s=new lr.ZP.OpenAI({apiKey:l,baseURL:t,dangerouslyAllowBrowser:!0});try{let l=await s.chat.completions.create({model:e,messages:[{role:"user",content:"Hi, this is a test message"}],mock_testing_fallbacks:!0});h.ZP.success((0,r.jsxs)("span",{children:["Test model=",(0,r.jsx)("strong",{children:e}),", received model=",(0,r.jsx)("strong",{children:l.model}),". See ",(0,r.jsx)("a",{href:"#",onClick:()=>window.open("https://docs.litellm.ai/docs/proxy/reliability","_blank"),style:{textDecoration:"underline",color:"blue"},children:"curl"})]}))}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lo=e=>{let{accessToken:l,userRole:t,userID:s,modelData:a}=e,[o,i]=(0,n.useState)({}),[c,d]=(0,n.useState)(!1),[m]=er.Z.useForm(),[u,x]=(0,n.useState)(null),p={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"};(0,n.useEffect)(()=>{l&&t&&s&&V(l,s,t).then(e=>{console.log("callbacks",e),i(e.router_settings)})},[l,t,s]);let j=async e=>{if(l){console.log("received key: ".concat(e)),console.log("routerSettings['fallbacks']: ".concat(o.fallbacks)),o.fallbacks.map(l=>(e in l&&delete l[e],l));try{await G(l,{router_settings:o}),i({...o}),h.ZP.success("Router settings updated successfully")}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}}},g=e=>{if(!l)return;console.log("router_settings",e);let t=Object.fromEntries(Object.entries(e).map(e=>{let[l,t]=e;if("routing_strategy_args"!==l){var s;return[l,(null===(s=document.querySelector('input[name="'.concat(l,'"]')))||void 0===s?void 0:s.value)||t]}return null}).filter(e=>null!==e));console.log("updatedVariables",t);try{G(l,{router_settings:t})}catch(e){h.ZP.error("Failed to update router settings: "+e,20)}h.ZP.success("router settings updated successfully")};return l?(0,r.jsx)("div",{className:"w-full mx-4",children:(0,r.jsxs)(eD.Z,{className:"gap-2 p-8 h-[75vh] w-full mt-2",children:[(0,r.jsxs)(ez.Z,{variant:"line",defaultValue:"1",children:[(0,r.jsx)(eU.Z,{value:"1",children:"General Settings"}),(0,r.jsx)(eU.Z,{value:"2",children:"Fallbacks"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:1,className:"gap-2 p-8 w-full mt-2",children:[(0,r.jsx)(et.Z,{children:"Router Settings"}),(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Setting"}),(0,r.jsx)(ev.Z,{children:"Value"})]})}),(0,r.jsx)(eb.Z,{children:Object.entries(o).filter(e=>{let[l,t]=e;return"fallbacks"!=l&&"context_window_fallbacks"!=l}).map(e=>{let[l,t]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsxs)(e_.Z,{children:[(0,r.jsx)(el.Z,{children:l}),(0,r.jsx)("p",{style:{fontSize:"0.65rem",color:"#808080",fontStyle:"italic"},className:"mt-1",children:p[l]})]}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)($.Z,{name:l,defaultValue:"object"==typeof t?JSON.stringify(t,null,2):t.toString()})})]},l)})})]})}),(0,r.jsx)(J.Z,{children:(0,r.jsx)(H.Z,{className:"mt-2",onClick:()=>g(o),children:"Save Changes"})})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Model Name"}),(0,r.jsx)(ev.Z,{children:"Fallbacks"})]})}),(0,r.jsx)(eb.Z,{children:o.fallbacks&&o.fallbacks.map((e,t)=>Object.entries(e).map(e=>{let[s,a]=e;return(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:s}),(0,r.jsx)(e_.Z,{children:Array.isArray(a)?a.join(", "):a}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(H.Z,{onClick:()=>ln(s,l),children:"Test Fallback"})}),(0,r.jsx)(e_.Z,{children:(0,r.jsx)(eZ.Z,{icon:ex.Z,size:"sm",onClick:()=>j(s)})})]},t.toString()+s)}))})]}),(0,r.jsx)(la,{models:(null==a?void 0:a.data)?a.data.map(e=>e.model_name):[],accessToken:l,routerSettings:o,setRouterSettings:i})]})]})]})}):null},li=t(67951),lc=e=>{let{}=e;return(0,r.jsx)(r.Fragment,{children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsxs)("div",{className:"mb-5",children:[(0,r.jsx)("p",{className:"text-2xl text-tremor-content-strong dark:text-dark-tremor-content-strong font-semibold",children:"OpenAI Compatible Proxy: API Reference"}),(0,r.jsx)(el.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,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{children:[(0,r.jsx)(eU.Z,{children:"OpenAI Python SDK"}),(0,r.jsx)(eU.Z,{children:"LlamaIndex"}),(0,r.jsx)(eU.Z,{children:"Langchain Py"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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="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,r.jsx)(eB.Z,{children:(0,r.jsx)(li.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 = "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 ld(e,l,t,s){console.log("isLocal:",!1);let a=window.location.origin,r=new lr.ZP.OpenAI({apiKey:s,baseURL:a,dangerouslyAllowBrowser:!0});try{for await(let s of(await r.chat.completions.create({model:t,stream:!0,messages:[{role:"user",content:e}]})))console.log(s),s.choices[0].delta.content&&l(s.choices[0].delta.content)}catch(e){h.ZP.error("Error occurred while generating model response. Please try again. Error: ".concat(e),20)}}var lm=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,[o,i]=(0,n.useState)(""),[c,d]=(0,n.useState)(""),[m,h]=(0,n.useState)([]),[u,x]=(0,n.useState)(void 0),[p,j]=(0,n.useState)([]);(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{let e=await v(l,a,s);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}));console.log(l),j(l),x(e.data[0].id)}}catch(e){console.error("Error fetching model info:",e)}})()},[l,a,s]);let g=(e,l)=>{h(t=>{let s=t[t.length-1];return s&&s.role===e?[...t.slice(0,t.length-1),{role:e,content:s.content+l}]:[...t,{role:e,content:l}]})},f=async()=>{if(""!==c.trim()&&o&&t&&s&&a){h(e=>[...e,{role:"user",content:c}]);try{u&&await ld(c,e=>g("assistant",e),u,o)}catch(e){console.error("Error fetching model response",e),g("assistant","Error fetching model response")}d("")}};if(s&&"Admin Viewer"==s){let{Title:e,Paragraph:l}=eR.default;return(0,r.jsxs)("div",{children:[(0,r.jsx)(e,{level:1,children:"Access Denied"}),(0,r.jsx)(l,{children:"Ask your proxy admin for access to test models"})]})}return(0,r.jsx)("div",{style:{width:"100%",position:"relative"},children:(0,r.jsx)(Y.Z,{className:"gap-2 p-8 h-[80vh] w-full mt-2",children:(0,r.jsx)(eg.Z,{children:(0,r.jsxs)(eD.Z,{children:[(0,r.jsx)(ez.Z,{children:(0,r.jsx)(eU.Z,{children:"Chat"})}),(0,r.jsx)(eq.Z,{children:(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)("div",{className:"sm:max-w-2xl",children:(0,r.jsxs)(Y.Z,{numItems:2,children:[(0,r.jsxs)(J.Z,{children:[(0,r.jsx)(el.Z,{children:"API Key"}),(0,r.jsx)($.Z,{placeholder:"Type API Key here",type:"password",onValueChange:i,value:o})]}),(0,r.jsxs)(J.Z,{className:"mx-2",children:[(0,r.jsx)(el.Z,{children:"Select Model:"}),(0,r.jsx)(ea.default,{placeholder:"Select a Model",onChange:e=>{console.log("selected ".concat(e)),x(e)},options:p,style:{width:"200px"}})]})]})}),(0,r.jsxs)(ew.Z,{className:"mt-5",style:{display:"block",maxHeight:"60vh",overflowY:"auto"},children:[(0,r.jsx)(ek.Z,{children:(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{})})}),(0,r.jsx)(eb.Z,{children:m.map((e,l)=>(0,r.jsx)(eS.Z,{children:(0,r.jsx)(e_.Z,{children:"".concat(e.role,": ").concat(e.content)})},l))})]}),(0,r.jsx)("div",{className:"mt-3",style:{position:"absolute",bottom:5,width:"95%"},children:(0,r.jsxs)("div",{className:"flex",children:[(0,r.jsx)($.Z,{type:"text",value:c,onChange:e=>d(e.target.value),placeholder:"Type your message..."}),(0,r.jsx)(H.Z,{onClick:f,className:"ml-2",children:"Send"})]})})]})})]})})})})},lh=t(33509),lu=t(95781);let{Sider:lx}=lh.default;var lp=e=>{let{setPage:l,userRole:t,defaultSelectedKey:s}=e;return"Admin Viewer"==t?(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"120px"},children:(0,r.jsx)(lx,{width:120,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["4"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:"API Keys"},"4"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:"Models"},"2"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:"Chat UI"},"3"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:"Usage"},"1")]})})}):(0,r.jsx)(lh.default,{style:{minHeight:"100vh",maxWidth:"145px"},children:(0,r.jsx)(lx,{width:145,children:(0,r.jsxs)(lu.Z,{mode:"inline",defaultSelectedKeys:s||["1"],style:{height:"100%",borderRight:0},children:[(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api-keys"),children:(0,r.jsx)(el.Z,{children:"API Keys"})},"1"),(0,r.jsx)(lu.Z.Item,{onClick:()=>l("llm-playground"),children:(0,r.jsx)(el.Z,{children:"Test Key"})},"3"),"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("models"),children:(0,r.jsx)(el.Z,{children:"Models"})},"2"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("usage"),children:(0,r.jsx)(el.Z,{children:"Usage"})},"4"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("teams"),children:(0,r.jsx)(el.Z,{children:"Teams"})},"6"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("users"),children:(0,r.jsx)(el.Z,{children:"Users"})},"5"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("settings"),children:(0,r.jsx)(el.Z,{children:"Logging & Alerts"})},"8"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("general-settings"),children:(0,r.jsx)(el.Z,{children:"Router Settings"})},"9"):null,"Admin"==t?(0,r.jsx)(lu.Z.Item,{onClick:()=>l("admin-panel"),children:(0,r.jsx)(el.Z,{children:"Admin"})},"7"):null,(0,r.jsx)(lu.Z.Item,{onClick:()=>l("api_ref"),children:(0,r.jsx)(el.Z,{children:"API Reference"})},"11")]})})})},lj=t(67989),lg=e=>{let{accessToken:l,token:t,userRole:s,userID:a}=e,o=new Date,[i,c]=(0,n.useState)([]),[d,m]=(0,n.useState)([]),[h,u]=(0,n.useState)([]),[x,p]=(0,n.useState)([]),[j,g]=(0,n.useState)([]),[f,y]=(0,n.useState)([]),[Z,w]=(0,n.useState)([]),[b,v]=(0,n.useState)([]),[S,T]=(0,n.useState)([]),[O,M]=(0,n.useState)([]),[R,L]=(0,n.useState)([]),[U,D]=(0,n.useState)(null),z=new Date(o.getFullYear(),o.getMonth(),1),B=new Date(o.getFullYear(),o.getMonth()+1,0),q=V(z),K=V(B);function V(e){let l=e.getFullYear(),t=e.getMonth()+1,s=e.getDate();return"".concat(l,"-").concat(t<10?"0"+t:t,"-").concat(s<10?"0"+s:s)}console.log("Start date is ".concat(q)),console.log("End date is ".concat(K)),(0,n.useEffect)(()=>{l&&t&&s&&a&&(async()=>{try{if(console.log("user role: ".concat(s)),"Admin"==s||"Admin Viewer"==s){let e=await I(l);c(e);let t=(await P(l)).map(e=>({key:(e.key_name||e.key_alias||e.api_key).substring(0,10),spend:e.total_spend}));m(t);let r=(await E(l)).map(e=>({key:e.model,spend:e.total_spend}));u(r);let n=await A(l);console.log("teamSpend",n),g(n.daily_spend),w(n.teams);let o=n.total_spend_per_team;o=o.map(e=>(e.name=e.team_id||"",e.value=e.total_spend||0,e)),v(o);let i=await N(l);y(i.top_10_tags);let d=(await _(l,a,s)).data;console.log("model groups in model dashboard",d);let h=[];for(let e=0;e{if(console.log("result from spend logs call",e),"daily_spend"in e){let l=e.daily_spend;console.log("daily spend",l),c(l);let t=e.top_api_keys;m(t)}else{let t=(await F(l,function(e){let l=[];e.forEach(e=>{Object.entries(e).forEach(e=>{let[t,s]=e;"spend"!==t&&"startTime"!==t&&"models"!==t&&"users"!==t&&l.push({key:t,spend:s})})}),l.sort((e,l)=>Number(l.spend)-Number(e.spend));let t=l.slice(0,5).map(e=>e.key);return console.log("topKeys: ".concat(Object.keys(t[0]))),t}(e))).info.map(e=>({key:(e.key_name||e.key_alias).substring(0,10),spend:e.spend}));m(t),p(function(e){let l={};e.forEach(e=>{Object.entries(e.users).forEach(e=>{let[t,s]=e;""!==t&&null!=t&&"None"!=t&&(l[t]||(l[t]=0),l[t]+=s)})});let t=Object.entries(l).map(e=>{let[l,t]=e;return{user_id:l,spend:t}});t.sort((e,l)=>l.spend-e.spend);let s=t.slice(0,5);return console.log("topKeys: ".concat(Object.values(s[0]))),s}(e)),c(e)}});let e=await k(l,a,s,null);console.log("Model metrics response:",e);let r=[...e].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(e),M(r)}catch(e){console.error("There was an error fetching the data",e)}})()},[l,t,s,a,q,K]);let G=async e=>{if(console.log("Updating model metrics for group:",e),l&&a&&s){D(e);try{let t=await k(l,a,s,e);console.log("Model metrics response:",t);let r=[...t].sort((e,l)=>l.avg_latency_seconds-e.avg_latency_seconds);console.log("Sorted by latency:",r),T(t),M(r)}catch(e){console.error("Failed to fetch model metrics",e)}}};return(0,r.jsxs)("div",{style:{width:"100%"},className:"p-8",children:[(0,r.jsx)(eE,{userID:a,userRole:s,accessToken:l,userSpend:null,selectedTeam:null}),(0,r.jsxs)(eD.Z,{children:[(0,r.jsxs)(ez.Z,{className:"mt-2",children:[(0,r.jsx)(eU.Z,{children:"All Up"}),(0,r.jsx)(eU.Z,{children:"Team Based Usage"}),(0,r.jsx)(eU.Z,{children:"Tag Based Usage"}),(0,r.jsx)(eU.Z,{children:"Model Based Usage"})]}),(0,r.jsxs)(eq.Z,{children:[(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Monthly Spend"}),(0,r.jsx)(ej.Z,{data:i,index:"date",categories:["spend"],colors:["blue"],valueFormatter:e=>"$ ".concat(new Intl.NumberFormat("us").format(e).toString()),yAxisWidth:100,tickGap:5})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top API Keys"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:d,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:80,tickGap:5,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Users"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:x,index:"user_id",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})}),(0,r.jsx)(J.Z,{numColSpan:1,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Top Models"}),(0,r.jsx)(ej.Z,{className:"mt-4 h-40",data:h,index:"key",categories:["spend"],colors:["blue"],yAxisWidth:200,layout:"vertical",showXAxis:!1,showLegend:!1})]})})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full",children:[(0,r.jsxs)(J.Z,{numColSpan:2,children:[(0,r.jsxs)(eg.Z,{className:"mb-2",children:[(0,r.jsx)(et.Z,{children:"Total Spend Per Team"}),(0,r.jsx)(lj.Z,{data:b})]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Daily Spend Per Team"}),(0,r.jsx)(ej.Z,{className:"h-72",data:j,showLegend:!0,index:"date",categories:Z,yAxisWidth:80,colors:["blue","green","yellow","red","purple"],stack:!0})]})]}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsx)(eB.Z,{children:(0,r.jsxs)(Y.Z,{numItems:2,className:"gap-2 h-[75vh] w-full mb-4",children:[(0,r.jsx)(J.Z,{numColSpan:2,children:(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Spend Per Tag - Last 30 Days"}),(0,r.jsxs)(el.Z,{children:["Get Started Tracking cost per tag ",(0,r.jsx)("a",{href:"https://docs.litellm.ai/docs/proxy/enterprise#tracking-spend-for-custom-tags",target:"_blank",children:"here"})]}),(0,r.jsxs)(ew.Z,{children:[(0,r.jsx)(ek.Z,{children:(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(ev.Z,{children:"Tag"}),(0,r.jsx)(ev.Z,{children:"Spend"}),(0,r.jsx)(ev.Z,{children:"Requests"})]})}),(0,r.jsx)(eb.Z,{children:f.map(e=>(0,r.jsxs)(eS.Z,{children:[(0,r.jsx)(e_.Z,{children:e.name}),(0,r.jsx)(e_.Z,{children:e.value}),(0,r.jsx)(e_.Z,{children:e.log_count})]},e.name))})]})]})}),(0,r.jsx)(J.Z,{numColSpan:2})]})}),(0,r.jsxs)(eB.Z,{children:[(0,r.jsx)(et.Z,{children:"Filter By Model Group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080"},children:"View how requests were load balanced within a model group"}),(0,r.jsx)("p",{style:{fontSize:"0.85rem",color:"#808080",fontStyle:"italic"},children:"(Beta feature) only supported for Azure Model Groups"}),(0,r.jsxs)(eA.Z,{className:"mb-4 mt-2",defaultValue:"all",children:[(0,r.jsx)(eN.Z,{value:"all",onClick:()=>G(null),children:"All Model Groups"}),R.map((e,l)=>(0,r.jsx)(eN.Z,{value:e,onClick:()=>G(e),children:e},l))]}),(0,r.jsxs)(eg.Z,{children:[(0,r.jsx)(et.Z,{children:"Number Requests per Model"}),(0,r.jsx)(ej.Z,{data:S,className:"h-[50vh]",index:"model",categories:["num_requests"],colors:["blue"],yAxisWidth:400,layout:"vertical",tickGap:5})]}),(0,r.jsxs)(eg.Z,{className:"mt-4",children:[(0,r.jsx)(et.Z,{children:"Latency Per Model"}),(0,r.jsx)(ej.Z,{data:O,className:"h-[50vh]",index:"model",categories:["avg_latency_seconds"],colors:["red"],yAxisWidth:400,layout:"vertical",tickGap:5})]})]})]})]})]})},lf=()=>{let{Title:e,Paragraph:l}=eR.default,[t,s]=(0,n.useState)(""),[a,i]=(0,n.useState)(null),[c,d]=(0,n.useState)(null),[h,u]=(0,n.useState)(null),[x,p]=(0,n.useState)(!0),j=(0,o.useSearchParams)(),[g,f]=(0,n.useState)({data:[]}),y=j.get("userID"),Z=j.get("token"),[w,b]=(0,n.useState)("api-keys"),[_,k]=(0,n.useState)(null);return(0,n.useEffect)(()=>{if(Z){let e=(0,eM.o)(Z);if(e){if(console.log("Decoded token:",e),console.log("Decoded key:",e.key),k(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"app_user":return"App User";default:return"Unknown Role"}}(e.user_role);console.log("Decoded user_role:",l),s(l),"Admin Viewer"==l&&b("usage")}else console.log("User role not defined");e.user_email?i(e.user_email):console.log("User Email is not set ".concat(e)),e.login_method?p("username_password"==e.login_method):console.log("User Email is not set ".concat(e))}}},[Z]),(0,r.jsx)(n.Suspense,{fallback:(0,r.jsx)("div",{children:"Loading..."}),children:(0,r.jsxs)("div",{className:"flex flex-col min-h-screen",children:[(0,r.jsx)(m,{userID:y,userRole:t,userEmail:a,showSSOBanner:x}),(0,r.jsxs)("div",{className:"flex flex-1 overflow-auto",children:[(0,r.jsx)("div",{className:"mt-8",children:(0,r.jsx)(lp,{setPage:b,userRole:t,defaultSelectedKey:null})}),"api-keys"==w?(0,r.jsx)(eL,{userID:y,userRole:t,teams:c,keys:h,setUserRole:s,userEmail:a,setUserEmail:i,setTeams:d,setKeys:u}):"models"==w?(0,r.jsx)(e8,{userID:y,userRole:t,token:Z,accessToken:_,modelData:g,setModelData:f}):"llm-playground"==w?(0,r.jsx)(lm,{userID:y,userRole:t,token:Z,accessToken:_}):"users"==w?(0,r.jsx)(e6,{userID:y,userRole:t,token:Z,keys:h,teams:c,accessToken:_,setKeys:u}):"teams"==w?(0,r.jsx)(e7,{teams:c,setTeams:d,searchParams:j,accessToken:_,userID:y,userRole:t}):"admin-panel"==w?(0,r.jsx)(le,{setTeams:d,searchParams:j,accessToken:_,showSSOBanner:x}):"api_ref"==w?(0,r.jsx)(lc,{}):"settings"==w?(0,r.jsx)(lt,{userID:y,userRole:t,accessToken:_}):"general-settings"==w?(0,r.jsx)(lo,{userID:y,userRole:t,accessToken:_,modelData:g}):(0,r.jsx)(lg,{userID:y,userRole:t,token:Z,accessToken:_})]})]})})}}},function(e){e.O(0,[447,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/PtTtxXIYvdjQsvRgdITlk/_buildManifest.js b/ui/litellm-dashboard/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_buildManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_buildManifest.js rename to ui/litellm-dashboard/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_buildManifest.js diff --git a/ui/litellm-dashboard/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_ssgManifest.js b/ui/litellm-dashboard/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_ssgManifest.js similarity index 100% rename from ui/litellm-dashboard/out/_next/static/PtTtxXIYvdjQsvRgdITlk/_ssgManifest.js rename to ui/litellm-dashboard/out/_next/static/kbGdRQFfI6W3bEwfzmJDI/_ssgManifest.js diff --git a/ui/litellm-dashboard/out/index.html b/ui/litellm-dashboard/out/index.html index cb197f11e23..0dbd09152f7 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 267d8e5baf0..70ccce5418b 100644 --- a/ui/litellm-dashboard/out/index.txt +++ b/ui/litellm-dashboard/out/index.txt @@ -1,7 +1,7 @@ 2:I[77831,[],""] -3:I[27125,["447","static/chunks/447-9f8d32190ff7d16d.js","931","static/chunks/app/page-781ca5f151d78d1d.js"],""] +3:I[27125,["447","static/chunks/447-9f8d32190ff7d16d.js","931","static/chunks/app/page-508c39694bd40fe9.js"],""] 4:I[5613,[],""] 5:I[31778,[],""] -0:["PtTtxXIYvdjQsvRgdITlk",[[["",{"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_c23dc8","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/5e699db73bf6f8c2.css","precedence":"next","crossOrigin":""}]],"$L6"]]]] +0:["kbGdRQFfI6W3bEwfzmJDI",[[["",{"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_c23dc8","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/5e699db73bf6f8c2.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 280148543fd35d1ea5a32f9fec9e21db91c33abb Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 17:36:05 -0700 Subject: [PATCH 55/62] fix(router.py): fix trailing slash handling for api base which contains `/v1` --- litellm/router.py | 2 ++ litellm/tests/test_router.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/litellm/router.py b/litellm/router.py index cd7c72c4a58..23618123f3c 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -1994,6 +1994,8 @@ class Router: # 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/" diff --git a/litellm/tests/test_router.py b/litellm/tests/test_router.py index 8659b5d6686..7520ac75f15 100644 --- a/litellm/tests/test_router.py +++ b/litellm/tests/test_router.py @@ -57,6 +57,7 @@ def test_router_num_retries_init(num_retries, max_retries): else: assert getattr(model_client, "max_retries") == 0 + @pytest.mark.parametrize( "timeout", [10, 1.0, httpx.Timeout(timeout=300.0, connect=20.0)] ) @@ -137,6 +138,7 @@ def test_router_azure_ai_studio_init(mistral_api_base): print(f"uri_reference: {uri_reference}") assert "/v1/" in uri_reference + assert uri_reference.count("v1") == 1 def test_exception_raising(): From d07713a2755fe1292557c8a9192f2fe7386da5fd Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 17:48:07 -0700 Subject: [PATCH 56/62] test: fix test --- litellm/tests/test_router_timeout.py | 1 + 1 file changed, 1 insertion(+) diff --git a/litellm/tests/test_router_timeout.py b/litellm/tests/test_router_timeout.py index 4f99d1a99ec..1126f6fb8bf 100644 --- a/litellm/tests/test_router_timeout.py +++ b/litellm/tests/test_router_timeout.py @@ -57,6 +57,7 @@ def test_router_timeouts(): redis_password=os.getenv("REDIS_PASSWORD"), redis_port=int(os.getenv("REDIS_PORT")), timeout=10, + num_retries=0, ) print("***** TPM SETTINGS *****") From a3257fd5d3f017a06486719079ad550e9508ffb0 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 18:39:42 -0700 Subject: [PATCH 57/62] test(test_router_init.py): fix test --- litellm/tests/test_router_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/tests/test_router_init.py b/litellm/tests/test_router_init.py index 13f7bd190c3..f0f0cc541ca 100644 --- a/litellm/tests/test_router_init.py +++ b/litellm/tests/test_router_init.py @@ -203,7 +203,7 @@ def test_timeouts_router(): }, }, ] - router = Router(model_list=model_list) + router = Router(model_list=model_list, num_retries=0) print("PASSED !") From 3e8d9fc80d8b593be30cfa54539013d753611daf Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 19:07:34 -0700 Subject: [PATCH 58/62] test: skip local test --- litellm/tests/test_embedding.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/litellm/tests/test_embedding.py b/litellm/tests/test_embedding.py index e9a86997b64..69ddb39ff65 100644 --- a/litellm/tests/test_embedding.py +++ b/litellm/tests/test_embedding.py @@ -483,6 +483,8 @@ def test_mistral_embeddings(): except Exception as e: pytest.fail(f"Error occurred: {e}") + +@pytest.mark.skip(reason="local test") def test_watsonx_embeddings(): try: litellm.set_verbose = True From 1543efd5d41ea901add5ae714997619ddbe246ed Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Sat, 27 Apr 2024 19:08:26 -0700 Subject: [PATCH 59/62] =?UTF-8?q?bump:=20version=201.35.30=20=E2=86=92=201?= =?UTF-8?q?.35.31?= 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 2bd77b6c97d..ae09ad3cbeb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm" -version = "1.35.30" +version = "1.35.31" description = "Library to easily interface with LLM API providers" authors = ["BerriAI"] license = "MIT" @@ -80,7 +80,7 @@ requires = ["poetry-core", "wheel"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "1.35.30" +version = "1.35.31" version_files = [ "pyproject.toml:^version" ] From b9c0b55e7c400c7b8449d3eec5c005e919e1044f Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sat, 27 Apr 2024 21:02:19 -0700 Subject: [PATCH 60/62] test: fix test - set num_retries=0 --- litellm/router.py | 3 ++- litellm/tests/test_router_fallbacks.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/litellm/router.py b/litellm/router.py index 23618123f3c..df4c2e046a7 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -50,7 +50,6 @@ class Router: model_names: List = [] cache_responses: Optional[bool] = False default_cache_time_seconds: int = 1 * 60 * 60 # 1 hour - num_retries: int = openai.DEFAULT_MAX_RETRIES tenacity = None leastbusy_logger: Optional[LeastBusyLoggingHandler] = None lowesttpm_logger: Optional[LowestTPMLoggingHandler] = None @@ -237,6 +236,8 @@ class Router: self.num_retries = num_retries elif litellm.num_retries is not None: self.num_retries = litellm.num_retries + else: + self.num_retries = openai.DEFAULT_MAX_RETRIES self.timeout = timeout or litellm.request_timeout diff --git a/litellm/tests/test_router_fallbacks.py b/litellm/tests/test_router_fallbacks.py index 51d9451a87e..364319929ec 100644 --- a/litellm/tests/test_router_fallbacks.py +++ b/litellm/tests/test_router_fallbacks.py @@ -831,6 +831,7 @@ def test_usage_based_routing_fallbacks(): routing_strategy="usage-based-routing", redis_host=os.environ["REDIS_HOST"], redis_port=os.environ["REDIS_PORT"], + num_retries=0, ) messages = [ From 1f6c342e9454cb1274e7e4183be24ebb48405091 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sun, 28 Apr 2024 09:45:01 -0700 Subject: [PATCH 61/62] test: fix test --- litellm/tests/test_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litellm/tests/test_completion.py b/litellm/tests/test_completion.py index 92cd377b021..fe4aa9c1c80 100644 --- a/litellm/tests/test_completion.py +++ b/litellm/tests/test_completion.py @@ -2722,7 +2722,7 @@ def test_unified_auth_params(provider, model, project, region_name, token): @pytest.mark.asyncio async def test_acompletion_watsonx(): litellm.set_verbose = True - model_name = "watsonx/deployment/" + os.getenv("WATSONX_DEPLOYMENT_ID") + model_name = "watsonx/ibm/granite-13b-chat-v2" print("testing watsonx") try: response = await litellm.acompletion( From f74a43aa78052af0924e9d88fb93cee9fb8343cf Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Sun, 28 Apr 2024 09:48:03 -0700 Subject: [PATCH 62/62] docs(vllm.md): update docs to tell people to check openai-compatible endpoint docs for vllm --- docs/my-website/docs/providers/vllm.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/my-website/docs/providers/vllm.md b/docs/my-website/docs/providers/vllm.md index b8285da7169..8c8f363f8ef 100644 --- a/docs/my-website/docs/providers/vllm.md +++ b/docs/my-website/docs/providers/vllm.md @@ -4,6 +4,13 @@ LiteLLM supports all models on VLLM. 🚀[Code Tutorial](https://github.com/BerriAI/litellm/blob/main/cookbook/VLLM_Model_Testing.ipynb) + +:::info + +To call a HOSTED VLLM Endpoint use [these docs](./openai_compatible.md) + +::: + ### Quick Start ``` pip install litellm vllm