From 85491a0babf742259420336356e355e70e22a4c2 Mon Sep 17 00:00:00 2001 From: exiao Date: Thu, 6 Feb 2025 19:16:28 -0500 Subject: [PATCH 01/74] Add Arize Cookbook for Turning on LiteLLM Proxy (#8336) * Add files via upload * Update arize_integration.md --- .../logging_observability/LiteLLM_Arize.ipynb | 172 ++++++++++++++++++ .../docs/observability/arize_integration.md | 2 +- 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 cookbook/logging_observability/LiteLLM_Arize.ipynb diff --git a/cookbook/logging_observability/LiteLLM_Arize.ipynb b/cookbook/logging_observability/LiteLLM_Arize.ipynb new file mode 100644 index 0000000000..72a082f874 --- /dev/null +++ b/cookbook/logging_observability/LiteLLM_Arize.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "4FbDOmcj2VkM" + }, + "source": [ + "## Use LiteLLM with Arize\n", + "https://docs.litellm.ai/docs/observability/arize_integration\n", + "\n", + "This method uses the litellm proxy to send the data to Arize. The callback is set in the litellm config below, instead of using OpenInference tracing." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "21W8Woog26Ns" + }, + "source": [ + "## Install Dependencies" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "xrjKLBxhxu2L" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: litellm in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (1.54.1)\n", + "Requirement already satisfied: aiohttp in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (3.11.10)\n", + "Requirement already satisfied: click in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (8.1.7)\n", + "Requirement already satisfied: httpx<0.28.0,>=0.23.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (0.27.2)\n", + "Requirement already satisfied: importlib-metadata>=6.8.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (8.5.0)\n", + "Requirement already satisfied: jinja2<4.0.0,>=3.1.2 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (3.1.4)\n", + "Requirement already satisfied: jsonschema<5.0.0,>=4.22.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (4.23.0)\n", + "Requirement already satisfied: openai>=1.55.3 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (1.57.1)\n", + "Requirement already satisfied: pydantic<3.0.0,>=2.0.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (2.10.3)\n", + "Requirement already satisfied: python-dotenv>=0.2.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (1.0.1)\n", + "Requirement already satisfied: requests<3.0.0,>=2.31.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (2.32.3)\n", + "Requirement already satisfied: tiktoken>=0.7.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (0.7.0)\n", + "Requirement already satisfied: tokenizers in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from litellm) (0.21.0)\n", + "Requirement already satisfied: anyio in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpx<0.28.0,>=0.23.0->litellm) (4.7.0)\n", + "Requirement already satisfied: certifi in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpx<0.28.0,>=0.23.0->litellm) (2024.8.30)\n", + "Requirement already satisfied: httpcore==1.* in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpx<0.28.0,>=0.23.0->litellm) (1.0.7)\n", + "Requirement already satisfied: idna in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpx<0.28.0,>=0.23.0->litellm) (3.10)\n", + "Requirement already satisfied: sniffio in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpx<0.28.0,>=0.23.0->litellm) (1.3.1)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from httpcore==1.*->httpx<0.28.0,>=0.23.0->litellm) (0.14.0)\n", + "Requirement already satisfied: zipp>=3.20 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from importlib-metadata>=6.8.0->litellm) (3.21.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from jinja2<4.0.0,>=3.1.2->litellm) (3.0.2)\n", + "Requirement already satisfied: attrs>=22.2.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (24.2.0)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (2024.10.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (0.35.1)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (0.22.3)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from openai>=1.55.3->litellm) (1.9.0)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from openai>=1.55.3->litellm) (0.6.1)\n", + "Requirement already satisfied: tqdm>4 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from openai>=1.55.3->litellm) (4.67.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.11 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from openai>=1.55.3->litellm) (4.12.2)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from pydantic<3.0.0,>=2.0.0->litellm) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.27.1 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from pydantic<3.0.0,>=2.0.0->litellm) (2.27.1)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.31.0->litellm) (3.4.0)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from requests<3.0.0,>=2.31.0->litellm) (2.0.7)\n", + "Requirement already satisfied: regex>=2022.1.18 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from tiktoken>=0.7.0->litellm) (2024.11.6)\n", + "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (2.4.4)\n", + "Requirement already satisfied: aiosignal>=1.1.2 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (1.3.1)\n", + "Requirement already satisfied: frozenlist>=1.1.1 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (1.5.0)\n", + "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (6.1.0)\n", + "Requirement already satisfied: propcache>=0.2.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (0.2.1)\n", + "Requirement already satisfied: yarl<2.0,>=1.17.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from aiohttp->litellm) (1.18.3)\n", + "Requirement already satisfied: huggingface-hub<1.0,>=0.16.4 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from tokenizers->litellm) (0.26.5)\n", + "Requirement already satisfied: filelock in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.16.4->tokenizers->litellm) (3.16.1)\n", + "Requirement already satisfied: fsspec>=2023.5.0 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.16.4->tokenizers->litellm) (2024.10.0)\n", + "Requirement already satisfied: packaging>=20.9 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.16.4->tokenizers->litellm) (24.2)\n", + "Requirement already satisfied: pyyaml>=5.1 in /Users/ericxiao/Documents/arize/.venv/lib/python3.11/site-packages (from huggingface-hub<1.0,>=0.16.4->tokenizers->litellm) (6.0.2)\n" + ] + } + ], + "source": [ + "!pip install litellm" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jHEu-TjZ29PJ" + }, + "source": [ + "## Set Env Variables" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "QWd9rTysxsWO" + }, + "outputs": [], + "source": [ + "import litellm\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "os.environ[\"ARIZE_SPACE_KEY\"] = getpass(\"Enter your Arize space key: \")\n", + "os.environ[\"ARIZE_API_KEY\"] = getpass(\"Enter your Arize API key: \")\n", + "os.environ['OPENAI_API_KEY']= getpass(\"Enter your OpenAI API key: \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's run a completion call and see the traces in Arize" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello! Nice to meet you, OpenAI. How can I assist you today?\n" + ] + } + ], + "source": [ + "# set arize as a callback, litellm will send the data to arize\n", + "litellm.callbacks = [\"arize\"]\n", + " \n", + "# openai call\n", + "response = litellm.completion(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[\n", + " {\"role\": \"user\", \"content\": \"Hi 👋 - i'm openai\"}\n", + " ]\n", + ")\n", + "print(response.choices[0].message.content)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/docs/my-website/docs/observability/arize_integration.md b/docs/my-website/docs/observability/arize_integration.md index a69d32e5b3..73122196b0 100644 --- a/docs/my-website/docs/observability/arize_integration.md +++ b/docs/my-website/docs/observability/arize_integration.md @@ -28,7 +28,7 @@ import litellm import os os.environ["ARIZE_SPACE_KEY"] = "" -os.environ["ARIZE_API_KEY"] = "" # defaults to litellm-completion +os.environ["ARIZE_API_KEY"] = "" # LLM API Keys os.environ['OPENAI_API_KEY']="" From 1b4f0f719245c15b29996964bda96bfd9479e488 Mon Sep 17 00:00:00 2001 From: Luis Sanchez Date: Thu, 6 Feb 2025 19:16:54 -0500 Subject: [PATCH 02/74] Add aistudio GEMINI 2.0 to model_prices_and_context_window.json (#8335) --- model_prices_and_context_window.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 71650cd42a..18d17d32ea 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3653,6 +3653,29 @@ "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash", "supports_tool_choice": true }, + "gemini/gemini-2.0-flash": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 8192, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_audio_token": 0.001, + "input_cost_per_token": 0.00015, + "output_cost_per_token": 0.0006, + "litellm_provider": "gemini", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_response_schema": true, + "supports_audio_output": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash" + }, "gemini-2.0-flash-001": { "max_tokens": 8192, "max_input_tokens": 1048576, From 15ac5f3c32c3913084cf564bb9e0d3b93824d7ed Mon Sep 17 00:00:00 2001 From: Wanis Elabbar <70503629+elabbarw@users.noreply.github.com> Date: Fri, 7 Feb 2025 00:17:29 +0000 Subject: [PATCH 03/74] Fix pricing for Gemini 2.0 Flash 001 (#8320) Model Type Price Price with Batch API Gemini 2.0 Flash 1M Input tokens $0.15 $0.075 1M Input audio tokens $1.00 $0.50 1M Output text tokens $0.60 $0.30 https://cloud.google.com/vertex-ai/generative-ai/pricing#token-based-pricing --- model_prices_and_context_window.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 18d17d32ea..c40c16b18f 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -3650,7 +3650,7 @@ "supports_vision": true, "supports_response_schema": true, "supports_audio_output": true, - "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash", + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing", "supports_tool_choice": true }, "gemini/gemini-2.0-flash": { @@ -3686,9 +3686,9 @@ "max_audio_length_hours": 8.4, "max_audio_per_prompt": 1, "max_pdf_size_mb": 30, - "input_cost_per_audio_token": 0.001, - "input_cost_per_token": 0.00015, - "output_cost_per_token": 0.0006, + "input_cost_per_audio_token": 0.000001, + "input_cost_per_token": 0.00000015, + "output_cost_per_token": 0.0000006, "litellm_provider": "vertex_ai-language-models", "mode": "chat", "supports_system_messages": true, @@ -3697,7 +3697,7 @@ "supports_response_schema": true, "supports_audio_output": true, "supports_tool_choice": true, - "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash" + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" }, "gemini-2.0-flash-thinking-exp": { "max_tokens": 8192, From 3ec9c28fb72fd9696bfb9a09656e4377a114fe37 Mon Sep 17 00:00:00 2001 From: Rok Benko <115651717+rokbenko@users.noreply.github.com> Date: Fri, 7 Feb 2025 01:19:32 +0100 Subject: [PATCH 04/74] Update local_debugging.md (#8308) --- docs/my-website/docs/debugging/local_debugging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/my-website/docs/debugging/local_debugging.md b/docs/my-website/docs/debugging/local_debugging.md index a9409bfab0..8a56d6c34a 100644 --- a/docs/my-website/docs/debugging/local_debugging.md +++ b/docs/my-website/docs/debugging/local_debugging.md @@ -1,5 +1,5 @@ # Local Debugging -There's 2 ways to do local debugging - `litellm.set_verbose=True` and by passing in a custom function `completion(...logger_fn=)`. Warning: Make sure to not use `set_verbose` in production. It logs API keys, which might end up in log files. +There's 2 ways to do local debugging - `litellm._turn_on_debug()` and by passing in a custom function `completion(...logger_fn=)`. Warning: Make sure to not use `_turn_on_debug()` in production. It logs API keys, which might end up in log files. ## Set Verbose @@ -8,7 +8,7 @@ This is good for getting print statements for everything litellm is doing. import litellm from litellm import completion -litellm.set_verbose=True # 👈 this is the 1-line change you need to make +litellm._turn_on_debug() # 👈 this is the 1-line change you need to make ## set ENV variables os.environ["OPENAI_API_KEY"] = "openai key" From b535c9bdc0388d20020bf5baf13af4814ae0d1b1 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 6 Feb 2025 18:02:26 -0800 Subject: [PATCH 05/74] (Bug Fix - Langfuse) - fix for when model response has `choices=[]` (#8339) * refactor _get_langfuse_input_output_content * test_langfuse_logging_completion_with_malformed_llm_response * fix _get_langfuse_input_output_content * fixes for langfuse linting * unit testing for get chat/text content for langfuse * fix _should_raise_content_policy_error --- litellm/integrations/langfuse/langfuse.py | 301 +++++++++++------- .../langfuse/langfuse_prompt_management.py | 6 +- litellm/litellm_core_utils/litellm_logging.py | 6 +- litellm/router.py | 5 +- .../completion_with_no_choices.json | 75 +++++ .../test_langfuse_e2e_test.py | 29 ++ .../test_langfuse_unit_tests.py | 49 ++- 7 files changed, 352 insertions(+), 119 deletions(-) create mode 100644 tests/logging_callback_tests/langfuse_expected_request_body/completion_with_no_choices.json diff --git a/litellm/integrations/langfuse/langfuse.py b/litellm/integrations/langfuse/langfuse.py index 125bf4e686..f990a316c4 100644 --- a/litellm/integrations/langfuse/langfuse.py +++ b/litellm/integrations/langfuse/langfuse.py @@ -3,7 +3,8 @@ import copy import os import traceback -from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast +from datetime import datetime +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, cast from packaging.version import Version @@ -13,9 +14,16 @@ from litellm.litellm_core_utils.redact_messages import redact_user_api_key_info from litellm.llms.custom_httpx.http_handler import _get_httpx_client from litellm.secret_managers.main import str_to_bool from litellm.types.integrations.langfuse import * +from litellm.types.llms.openai import HttpxBinaryResponseContent from litellm.types.utils import ( + EmbeddingResponse, + ImageResponse, + ModelResponse, + RerankResponse, StandardLoggingPayload, StandardLoggingPromptManagementMetadata, + TextCompletionResponse, + TranscriptionResponse, ) if TYPE_CHECKING: @@ -150,19 +158,29 @@ class LangFuseLogger: return metadata - def _old_log_event( # noqa: PLR0915 + def log_event_on_langfuse( self, - kwargs, - response_obj, - start_time, - end_time, - user_id, - print_verbose, - level="DEFAULT", - status_message=None, + kwargs: dict, + response_obj: Union[ + None, + dict, + EmbeddingResponse, + ModelResponse, + TextCompletionResponse, + ImageResponse, + TranscriptionResponse, + RerankResponse, + HttpxBinaryResponseContent, + ], + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + user_id: Optional[str] = None, + level: str = "DEFAULT", + status_message: Optional[str] = None, ) -> dict: - # Method definition - + """ + Logs a success or error event on Langfuse + """ try: verbose_logger.debug( f"Langfuse Logging - Enters logging function for model {kwargs}" @@ -198,66 +216,13 @@ class LangFuseLogger: # if casting value to str fails don't block logging pass - # end of processing langfuse ######################## - if ( - level == "ERROR" - and status_message is not None - and isinstance(status_message, str) - ): - input = prompt - output = status_message - elif response_obj is not None and ( - kwargs.get("call_type", None) == "embedding" - or isinstance(response_obj, litellm.EmbeddingResponse) - ): - input = prompt - output = None - elif response_obj is not None and isinstance( - response_obj, litellm.ModelResponse - ): - input = prompt - output = response_obj["choices"][0]["message"].json() - elif response_obj is not None and isinstance( - response_obj, litellm.HttpxBinaryResponseContent - ): - input = prompt - output = "speech-output" - elif response_obj is not None and isinstance( - response_obj, litellm.TextCompletionResponse - ): - input = prompt - output = response_obj.choices[0].text - elif response_obj is not None and isinstance( - response_obj, litellm.ImageResponse - ): - input = prompt - output = response_obj["data"] - elif response_obj is not None and isinstance( - response_obj, litellm.TranscriptionResponse - ): - input = prompt - output = response_obj["text"] - elif response_obj is not None and isinstance( - response_obj, litellm.RerankResponse - ): - input = prompt - output = response_obj.results - elif ( - kwargs.get("call_type") is not None - and kwargs.get("call_type") == "_arealtime" - and response_obj is not None - and isinstance(response_obj, list) - ): - input = kwargs.get("input") - output = response_obj - elif ( - kwargs.get("call_type") is not None - and kwargs.get("call_type") == "pass_through_endpoint" - and response_obj is not None - and isinstance(response_obj, dict) - ): - input = prompt - output = response_obj.get("response", "") + input, output = self._get_langfuse_input_output_content( + kwargs=kwargs, + response_obj=response_obj, + prompt=prompt, + level=level, + status_message=status_message, + ) verbose_logger.debug( f"OUTPUT IN LANGFUSE: {output}; original: {response_obj}" ) @@ -265,31 +230,30 @@ class LangFuseLogger: generation_id = None if self._is_langfuse_v2(): trace_id, generation_id = self._log_langfuse_v2( - user_id, - metadata, - litellm_params, - output, - start_time, - end_time, - kwargs, - optional_params, - input, - response_obj, - level, - print_verbose, - litellm_call_id, + user_id=user_id, + metadata=metadata, + litellm_params=litellm_params, + output=output, + start_time=start_time, + end_time=end_time, + kwargs=kwargs, + optional_params=optional_params, + input=input, + response_obj=response_obj, + level=level, + litellm_call_id=litellm_call_id, ) elif response_obj is not None: self._log_langfuse_v1( - user_id, - metadata, - output, - start_time, - end_time, - kwargs, - optional_params, - input, - response_obj, + user_id=user_id, + metadata=metadata, + output=output, + start_time=start_time, + end_time=end_time, + kwargs=kwargs, + optional_params=optional_params, + input=input, + response_obj=response_obj, ) verbose_logger.debug( f"Langfuse Layer Logging - final response object: {response_obj}" @@ -303,11 +267,108 @@ class LangFuseLogger: ) return {"trace_id": None, "generation_id": None} + def _get_langfuse_input_output_content( + self, + kwargs: dict, + response_obj: Union[ + None, + dict, + EmbeddingResponse, + ModelResponse, + TextCompletionResponse, + ImageResponse, + TranscriptionResponse, + RerankResponse, + HttpxBinaryResponseContent, + ], + prompt: dict, + level: str, + status_message: Optional[str], + ) -> Tuple[Optional[dict], Optional[Union[str, dict, list]]]: + """ + Get the input and output content for Langfuse logging + + Args: + kwargs: The keyword arguments passed to the function + response_obj: The response object returned by the function + prompt: The prompt used to generate the response + level: The level of the log message + status_message: The status message of the log message + + Returns: + input: The input content for Langfuse logging + output: The output content for Langfuse logging + """ + input = None + output: Optional[Union[str, dict, List[Any]]] = None + if ( + level == "ERROR" + and status_message is not None + and isinstance(status_message, str) + ): + input = prompt + output = status_message + elif response_obj is not None and ( + kwargs.get("call_type", None) == "embedding" + or isinstance(response_obj, litellm.EmbeddingResponse) + ): + input = prompt + output = None + elif response_obj is not None and isinstance( + response_obj, litellm.ModelResponse + ): + input = prompt + output = self._get_chat_content_for_langfuse(response_obj) + elif response_obj is not None and isinstance( + response_obj, litellm.HttpxBinaryResponseContent + ): + input = prompt + output = "speech-output" + elif response_obj is not None and isinstance( + response_obj, litellm.TextCompletionResponse + ): + input = prompt + output = self._get_text_completion_content_for_langfuse(response_obj) + elif response_obj is not None and isinstance( + response_obj, litellm.ImageResponse + ): + input = prompt + output = response_obj.get("data", None) + elif response_obj is not None and isinstance( + response_obj, litellm.TranscriptionResponse + ): + input = prompt + output = response_obj.get("text", None) + elif response_obj is not None and isinstance( + response_obj, litellm.RerankResponse + ): + input = prompt + output = response_obj.results + elif ( + kwargs.get("call_type") is not None + and kwargs.get("call_type") == "_arealtime" + and response_obj is not None + and isinstance(response_obj, list) + ): + input = kwargs.get("input") + output = response_obj + elif ( + kwargs.get("call_type") is not None + and kwargs.get("call_type") == "pass_through_endpoint" + and response_obj is not None + and isinstance(response_obj, dict) + ): + input = prompt + output = response_obj.get("response", "") + return input, output + async def _async_log_event( - self, kwargs, response_obj, start_time, end_time, user_id, print_verbose + self, kwargs, response_obj, start_time, end_time, user_id ): """ - TODO: support async calls when langfuse is truly async + Langfuse SDK uses a background thread to log events + + This approach does not impact latency and runs in the background """ def _is_langfuse_v2(self): @@ -361,19 +422,18 @@ class LangFuseLogger: def _log_langfuse_v2( # noqa: PLR0915 self, - user_id, - metadata, - litellm_params, - output, - start_time, - end_time, - kwargs, - optional_params, - input, + user_id: Optional[str], + metadata: dict, + litellm_params: dict, + output: Optional[Union[str, dict, list]], + start_time: Optional[datetime], + end_time: Optional[datetime], + kwargs: dict, + optional_params: dict, + input: Optional[dict], response_obj, - level, - print_verbose, - litellm_call_id, + level: str, + litellm_call_id: Optional[str], ) -> tuple: verbose_logger.debug("Langfuse Layer Logging - logging to langfuse v2") @@ -657,6 +717,31 @@ class LangFuseLogger: verbose_logger.error(f"Langfuse Layer Error - {traceback.format_exc()}") return None, None + @staticmethod + def _get_chat_content_for_langfuse( + response_obj: ModelResponse, + ): + """ + Get the chat content for Langfuse logging + """ + if response_obj.choices and len(response_obj.choices) > 0: + output = response_obj["choices"][0]["message"].json() + return output + else: + return None + + @staticmethod + def _get_text_completion_content_for_langfuse( + response_obj: TextCompletionResponse, + ): + """ + Get the text completion content for Langfuse logging + """ + if response_obj.choices and len(response_obj.choices) > 0: + return response_obj.choices[0].text + else: + return None + @staticmethod def _get_langfuse_tags( standard_logging_object: Optional[StandardLoggingPayload], diff --git a/litellm/integrations/langfuse/langfuse_prompt_management.py b/litellm/integrations/langfuse/langfuse_prompt_management.py index faa4a63491..cc2a6cf80d 100644 --- a/litellm/integrations/langfuse/langfuse_prompt_management.py +++ b/litellm/integrations/langfuse/langfuse_prompt_management.py @@ -247,13 +247,12 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge standard_callback_dynamic_params=standard_callback_dynamic_params, in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, ) - langfuse_logger_to_use._old_log_event( + langfuse_logger_to_use.log_event_on_langfuse( kwargs=kwargs, response_obj=response_obj, start_time=start_time, end_time=end_time, user_id=kwargs.get("user", None), - print_verbose=None, ) async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time): @@ -271,12 +270,11 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge ) if standard_logging_object is None: return - langfuse_logger_to_use._old_log_event( + langfuse_logger_to_use.log_event_on_langfuse( start_time=start_time, end_time=end_time, response_obj=None, user_id=kwargs.get("user", None), - print_verbose=None, status_message=standard_logging_object["error_str"], level="ERROR", kwargs=kwargs, diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 45b63177b9..28182b75ac 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -1247,13 +1247,12 @@ class Logging(LiteLLMLoggingBaseClass): in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, ) if langfuse_logger_to_use is not None: - _response = langfuse_logger_to_use._old_log_event( + _response = langfuse_logger_to_use.log_event_on_langfuse( kwargs=kwargs, response_obj=result, start_time=start_time, end_time=end_time, user_id=kwargs.get("user", None), - print_verbose=print_verbose, ) if _response is not None and isinstance(_response, dict): _trace_id = _response.get("trace_id", None) @@ -1957,12 +1956,11 @@ class Logging(LiteLLMLoggingBaseClass): standard_callback_dynamic_params=self.standard_callback_dynamic_params, in_memory_dynamic_logger_cache=in_memory_dynamic_logger_cache, ) - _response = langfuse_logger_to_use._old_log_event( + _response = langfuse_logger_to_use.log_event_on_langfuse( start_time=start_time, end_time=end_time, response_obj=None, user_id=kwargs.get("user", None), - print_verbose=print_verbose, status_message=str(exception), level="ERROR", kwargs=self.model_call_details, diff --git a/litellm/router.py b/litellm/router.py index faa7cd4a6b..b61c30dd57 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -3699,8 +3699,9 @@ class Router: Else, original response is returned. """ - if response.choices[0].finish_reason != "content_filter": - return False + if response.choices and len(response.choices) > 0: + if response.choices[0].finish_reason != "content_filter": + return False content_policy_fallbacks = kwargs.get( "content_policy_fallbacks", self.content_policy_fallbacks diff --git a/tests/logging_callback_tests/langfuse_expected_request_body/completion_with_no_choices.json b/tests/logging_callback_tests/langfuse_expected_request_body/completion_with_no_choices.json new file mode 100644 index 0000000000..0683ff9ba9 --- /dev/null +++ b/tests/logging_callback_tests/langfuse_expected_request_body/completion_with_no_choices.json @@ -0,0 +1,75 @@ +{ + "batch": [ + { + "id": "1f1d7517-4602-4c59-a322-7fc0306f1b7a", + "type": "trace-create", + "body": { + "id": "litellm-test-dbadfdfc-f4e7-4f05-8992-984c37359166", + "timestamp": "2025-02-07T00:23:27.669634Z", + "name": "litellm-acompletion", + "input": { + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }, + "tags": [] + }, + "timestamp": "2025-02-07T00:23:27.669809Z" + }, + { + "id": "fbe610b6-f500-4c7d-8e34-d40a0e8c487b", + "type": "generation-create", + "body": { + "traceId": "litellm-test-dbadfdfc-f4e7-4f05-8992-984c37359166", + "name": "litellm-acompletion", + "startTime": "2025-02-06T16:23:27.220129-08:00", + "metadata": { + "hidden_params": { + "model_id": null, + "cache_key": null, + "api_base": "https://api.openai.com", + "response_cost": 3.5e-05, + "additional_headers": {}, + "litellm_overhead_time_ms": null + }, + "litellm_response_cost": 3.5e-05, + "cache_hit": false, + "requester_metadata": {} + }, + "input": { + "messages": [ + { + "role": "user", + "content": "Hello!" + } + ] + }, + "level": "DEFAULT", + "id": "time-16-23-27-220129_chatcmpl-565360d7-965f-4533-9c09-db789af77a7d", + "endTime": "2025-02-06T16:23:27.644253-08:00", + "completionStartTime": "2025-02-06T16:23:27.644253-08:00", + "model": "gpt-3.5-turbo", + "modelParameters": { + "extra_body": "{}" + }, + "usage": { + "input": 10, + "output": 10, + "unit": "TOKENS", + "totalCost": 3.5e-05 + } + }, + "timestamp": "2025-02-07T00:23:27.670175Z" + } + ], + "metadata": { + "batch_size": 2, + "sdk_integration": "litellm", + "sdk_name": "python", + "sdk_version": "2.44.1", + "public_key": "pk-lf-e02aaea3-8668-4c9f-8c69-771a4ea1f5c9" + } +} \ No newline at end of file diff --git a/tests/logging_callback_tests/test_langfuse_e2e_test.py b/tests/logging_callback_tests/test_langfuse_e2e_test.py index 79197d6c25..b46d8764dd 100644 --- a/tests/logging_callback_tests/test_langfuse_e2e_test.py +++ b/tests/logging_callback_tests/test_langfuse_e2e_test.py @@ -352,3 +352,32 @@ class TestLangfuseLogging: response_json_file, setup["trace_id"], ) + + @pytest.mark.asyncio + async def test_langfuse_logging_completion_with_malformed_llm_response( + self, mock_setup + ): + """Test Langfuse logging for chat completion with malformed LLM response""" + setup = await mock_setup # Await the fixture + litellm._turn_on_debug() + with patch("httpx.Client.post", setup["mock_post"]): + mock_response = litellm.ModelResponse( + choices=[], + usage=litellm.Usage( + prompt_tokens=10, + completion_tokens=10, + total_tokens=20, + ), + model="gpt-3.5-turbo", + object="chat.completion", + created=1723081200, + ).model_dump() + await litellm.acompletion( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "Hello!"}], + mock_response=mock_response, + metadata={"trace_id": setup["trace_id"]}, + ) + await self._verify_langfuse_call( + setup["mock_post"], "completion_with_no_choices.json", setup["trace_id"] + ) diff --git a/tests/logging_callback_tests/test_langfuse_unit_tests.py b/tests/logging_callback_tests/test_langfuse_unit_tests.py index e9c255c1a3..16ed464fff 100644 --- a/tests/logging_callback_tests/test_langfuse_unit_tests.py +++ b/tests/logging_callback_tests/test_langfuse_unit_tests.py @@ -21,6 +21,11 @@ from litellm.types.utils import ( StandardLoggingMetadata, StandardLoggingHiddenParams, StandardCallbackDynamicParams, + ModelResponse, + Choices, + Message, + TextCompletionResponse, + TextChoices, ) @@ -294,7 +299,6 @@ def test_get_langfuse_tags(): assert result == [] - @patch.dict(os.environ, {}, clear=True) # Start with empty environment def test_get_langfuse_flush_interval(): """ @@ -316,6 +320,7 @@ def test_get_langfuse_flush_interval(): ) assert result == 120 + def test_langfuse_e2e_sync(monkeypatch): from litellm import completion import litellm @@ -343,3 +348,45 @@ def test_langfuse_e2e_sync(monkeypatch): assert langfuse_mock.called + +def test_get_chat_content_for_langfuse(): + """ + Test that _get_chat_content_for_langfuse correctly extracts content from chat completion responses + """ + # Test with valid response + mock_response = ModelResponse( + choices=[Choices(message=Message(role="assistant", content="Hello world"))] + ) + + result = LangFuseLogger._get_chat_content_for_langfuse(mock_response) + assert result == { + "content": "Hello world", + "role": "assistant", + "tool_calls": None, + "function_call": None, + } + + # Test with empty choices + mock_response = ModelResponse(choices=[]) + result = LangFuseLogger._get_chat_content_for_langfuse(mock_response) + assert result is None + + +def test_get_text_completion_content_for_langfuse(): + """ + Test that _get_text_completion_content_for_langfuse correctly extracts content from text completion responses + """ + # Test with valid response + mock_response = TextCompletionResponse(choices=[TextChoices(text="Hello world")]) + result = LangFuseLogger._get_text_completion_content_for_langfuse(mock_response) + assert result == "Hello world" + + # Test with empty choices + mock_response = TextCompletionResponse(choices=[]) + result = LangFuseLogger._get_text_completion_content_for_langfuse(mock_response) + assert result is None + + # Test with no choices field + mock_response = TextCompletionResponse() + result = LangFuseLogger._get_text_completion_content_for_langfuse(mock_response) + assert result is None From fac1d2ccef8a26c2cdc92b8e5bf2243fa7ec30db Mon Sep 17 00:00:00 2001 From: Anton Abilov Date: Thu, 6 Feb 2025 21:05:49 -0500 Subject: [PATCH 06/74] Fixed meta llama 3.3 key for Databricks API (#8093) See correct key reference here: https://docs.databricks.com/en/machine-learning/model-serving/foundation-model-overview.html#pay-per-token --- litellm/model_prices_and_context_window_backup.json | 2 +- model_prices_and_context_window.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 71650cd42a..0d5fac5669 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -8722,7 +8722,7 @@ "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, "supports_tool_choice": true }, - "databricks/meta-llama-3.3-70b-instruct": { + "databricks/databricks-meta-llama-3-3-70b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index c40c16b18f..e4b2d84639 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -8745,7 +8745,7 @@ "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, "supports_tool_choice": true }, - "databricks/meta-llama-3.3-70b-instruct": { + "databricks/databricks-meta-llama-3-3-70b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, "max_output_tokens": 128000, From f031926b823c4f32f36097b9e826610a604bbb97 Mon Sep 17 00:00:00 2001 From: Krish Dholakia Date: Thu, 6 Feb 2025 18:13:46 -0800 Subject: [PATCH 07/74] fix(utils.py): handle key error in msg validation (#8325) * fix(utils.py): handle key error in msg validation * Support running Aim Guard during LLM call (#7918) * support running Aim Guard during LLM call * Rename header * adjust docs and fix type annotations * fix(timeout.md): doc fix for openai example on dynamic timeouts --------- Co-authored-by: Tomer Bin <117278227+hxtomer@users.noreply.github.com> --- .../docs/proxy/guardrails/aim_security.md | 2 +- docs/my-website/docs/proxy/timeout.md | 2 +- .../proxy/guardrails/guardrail_hooks/aim.py | 40 +++++++++++++------ litellm/utils.py | 4 ++ tests/litellm_utils_tests/test_utils.py | 12 ++++++ tests/local_testing/test_aim_guardrails.py | 17 +++++--- 6 files changed, 56 insertions(+), 21 deletions(-) diff --git a/docs/my-website/docs/proxy/guardrails/aim_security.md b/docs/my-website/docs/proxy/guardrails/aim_security.md index d588afa424..3de933c0b7 100644 --- a/docs/my-website/docs/proxy/guardrails/aim_security.md +++ b/docs/my-website/docs/proxy/guardrails/aim_security.md @@ -37,7 +37,7 @@ guardrails: - guardrail_name: aim-protected-app litellm_params: guardrail: aim - mode: pre_call + mode: pre_call # 'during_call' is also available api_key: os.environ/AIM_API_KEY api_base: os.environ/AIM_API_BASE # Optional, use only when using a self-hosted Aim Outpost ``` diff --git a/docs/my-website/docs/proxy/timeout.md b/docs/my-website/docs/proxy/timeout.md index 2bf93298fe..85428ae53e 100644 --- a/docs/my-website/docs/proxy/timeout.md +++ b/docs/my-website/docs/proxy/timeout.md @@ -166,7 +166,7 @@ response = client.chat.completions.create( {"role": "user", "content": "what color is red"} ], logit_bias={12481: 100}, - timeout=1 + extra_body={"timeout": 1} # 👈 KEY CHANGE ) print(response) diff --git a/litellm/proxy/guardrails/guardrail_hooks/aim.py b/litellm/proxy/guardrails/guardrail_hooks/aim.py index 5f3ec9e880..91d19e277c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/aim.py +++ b/litellm/proxy/guardrails/guardrail_hooks/aim.py @@ -6,7 +6,7 @@ # +-------------------------------------------------------------+ import os -from typing import Literal, Optional +from typing import Literal, Optional, Union from fastapi import HTTPException @@ -25,12 +25,8 @@ class AimGuardrailMissingSecrets(Exception): class AimGuardrail(CustomGuardrail): - def __init__( - self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs - ): - self.async_handler = get_async_httpx_client( - llm_provider=httpxSpecialProvider.GuardrailCallback - ) + def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs): + self.async_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.GuardrailCallback) self.api_key = api_key or os.environ.get("AIM_API_KEY") if not self.api_key: msg = ( @@ -38,9 +34,7 @@ class AimGuardrail(CustomGuardrail): "pass it as a parameter to the guardrail in the config file" ) raise AimGuardrailMissingSecrets(msg) - self.api_base = ( - api_base or os.environ.get("AIM_API_BASE") or "https://api.aim.security" - ) + self.api_base = api_base or os.environ.get("AIM_API_BASE") or "https://api.aim.security" super().__init__(**kwargs) async def async_pre_call_hook( @@ -58,11 +52,32 @@ class AimGuardrail(CustomGuardrail): "pass_through_endpoint", "rerank", ], - ) -> Exception | str | dict | None: + ) -> Union[Exception, str, dict, None]: verbose_proxy_logger.debug("Inside AIM Pre-Call Hook") + await self.call_aim_guardrail(data, hook="pre_call") + return data + + async def async_moderation_hook( + self, + data: dict, + user_api_key_dict: UserAPIKeyAuth, + call_type: Literal[ + "completion", + "embeddings", + "image_generation", + "moderation", + "audio_transcription", + ], + ) -> Union[Exception, str, dict, None]: + verbose_proxy_logger.debug("Inside AIM Moderation Hook") + + await self.call_aim_guardrail(data, hook="moderation") + return data + + async def call_aim_guardrail(self, data: dict, hook: str) -> None: user_email = data.get("metadata", {}).get("headers", {}).get("x-aim-user-email") - headers = {"Authorization": f"Bearer {self.api_key}"} | ( + headers = {"Authorization": f"Bearer {self.api_key}", "x-aim-litellm-hook": hook} | ( {"x-aim-user-email": user_email} if user_email else {} ) response = await self.async_handler.post( @@ -80,4 +95,3 @@ class AimGuardrail(CustomGuardrail): ) if detected: raise HTTPException(status_code=400, detail=res["detection_message"]) - return data diff --git a/litellm/utils.py b/litellm/utils.py index 7e66ad2b22..d181791515 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5884,6 +5884,10 @@ def validate_chat_completion_user_messages(messages: List[AllMessageValues]): if item.get("type") not in ValidUserMessageContentTypes: raise Exception("invalid content type") except Exception as e: + if isinstance(e, KeyError): + raise Exception( + f"Invalid message={m} at index {idx}. Please ensure all messages are valid OpenAI chat completion messages." + ) if "invalid content type" in str(e): raise Exception( f"Invalid user message={m} at index {idx}. Please ensure all user messages are valid OpenAI chat completion messages." diff --git a/tests/litellm_utils_tests/test_utils.py b/tests/litellm_utils_tests/test_utils.py index 4f5e1e2737..4a2f63b51d 100644 --- a/tests/litellm_utils_tests/test_utils.py +++ b/tests/litellm_utils_tests/test_utils.py @@ -1850,3 +1850,15 @@ def test_dict_to_response_format_helper(): "ref_template": "/$defs/{model}", } _dict_to_response_format_helper(**args) + + +def test_validate_user_messages_invalid_content_type(): + from litellm.utils import validate_chat_completion_user_messages + + messages = [{"content": [{"type": "invalid_type", "text": "Hello"}]}] + + with pytest.raises(Exception) as e: + validate_chat_completion_user_messages(messages) + + assert "Invalid message" in str(e) + print(e) diff --git a/tests/local_testing/test_aim_guardrails.py b/tests/local_testing/test_aim_guardrails.py index b68140d37b..d43156fb19 100644 --- a/tests/local_testing/test_aim_guardrails.py +++ b/tests/local_testing/test_aim_guardrails.py @@ -55,15 +55,15 @@ def test_aim_guard_config_no_api_key(): @pytest.mark.asyncio -async def test_callback(): +@pytest.mark.parametrize("mode", ["pre_call", "during_call"]) +async def test_callback(mode: str): init_guardrails_v2( all_guardrails=[ { "guardrail_name": "gibberish-guard", "litellm_params": { "guardrail": "aim", - "guard_name": "gibberish_guard", - "mode": "pre_call", + "mode": mode, "api_key": "hs-aim-key", }, } @@ -89,6 +89,11 @@ async def test_callback(): request=Request(method="POST", url="http://aim"), ), ): - await aim_guardrail.async_pre_call_hook( - data=data, cache=DualCache(), user_api_key_dict=UserAPIKeyAuth(), call_type="completion" - ) + if mode == "pre_call": + await aim_guardrail.async_pre_call_hook( + data=data, cache=DualCache(), user_api_key_dict=UserAPIKeyAuth(), call_type="completion" + ) + else: + await aim_guardrail.async_moderation_hook( + data=data, user_api_key_dict=UserAPIKeyAuth(), call_type="completion" + ) From d2fec8bf133423c2c7a3555c77eaf0c6420b9303 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 6 Feb 2025 18:21:56 -0800 Subject: [PATCH 08/74] databricks/meta-llama-3.3-70b-instruct --- ...odel_prices_and_context_window_backup.json | 44 +++++++++++++++++-- model_prices_and_context_window.json | 15 +++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 0d5fac5669..a613b29a4d 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -3650,10 +3650,10 @@ "supports_vision": true, "supports_response_schema": true, "supports_audio_output": true, - "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash", + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing", "supports_tool_choice": true }, - "gemini-2.0-flash-001": { + "gemini/gemini-2.0-flash": { "max_tokens": 8192, "max_input_tokens": 1048576, "max_output_tokens": 8192, @@ -3666,7 +3666,7 @@ "input_cost_per_audio_token": 0.001, "input_cost_per_token": 0.00015, "output_cost_per_token": 0.0006, - "litellm_provider": "vertex_ai-language-models", + "litellm_provider": "gemini", "mode": "chat", "supports_system_messages": true, "supports_function_calling": true, @@ -3676,6 +3676,29 @@ "supports_tool_choice": true, "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-2.0-flash" }, + "gemini-2.0-flash-001": { + "max_tokens": 8192, + "max_input_tokens": 1048576, + "max_output_tokens": 8192, + "max_images_per_prompt": 3000, + "max_videos_per_prompt": 10, + "max_video_length": 1, + "max_audio_length_hours": 8.4, + "max_audio_per_prompt": 1, + "max_pdf_size_mb": 30, + "input_cost_per_audio_token": 0.000001, + "input_cost_per_token": 0.00000015, + "output_cost_per_token": 0.0000006, + "litellm_provider": "vertex_ai-language-models", + "mode": "chat", + "supports_system_messages": true, + "supports_function_calling": true, + "supports_vision": true, + "supports_response_schema": true, + "supports_audio_output": true, + "supports_tool_choice": true, + "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" + }, "gemini-2.0-flash-thinking-exp": { "max_tokens": 8192, "max_input_tokens": 1048576, @@ -8722,6 +8745,21 @@ "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, "supports_tool_choice": true }, + "databricks/meta-llama-3.3-70b-instruct": { + "max_tokens": 128000, + "max_input_tokens": 128000, + "max_output_tokens": 128000, + "input_cost_per_token": 0.00000100002, + "input_dbu_cost_per_token": 0.000014286, + "output_cost_per_token": 0.00000299999, + "output_dbu_cost_per_token": 0.000042857, + "litellm_provider": "databricks", + "mode": "chat", + "source": "https://www.databricks.com/product/pricing/foundation-model-serving", + "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, + "supports_tool_choice": true + + }, "databricks/databricks-meta-llama-3-3-70b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index e4b2d84639..a613b29a4d 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -8745,6 +8745,21 @@ "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, "supports_tool_choice": true }, + "databricks/meta-llama-3.3-70b-instruct": { + "max_tokens": 128000, + "max_input_tokens": 128000, + "max_output_tokens": 128000, + "input_cost_per_token": 0.00000100002, + "input_dbu_cost_per_token": 0.000014286, + "output_cost_per_token": 0.00000299999, + "output_dbu_cost_per_token": 0.000042857, + "litellm_provider": "databricks", + "mode": "chat", + "source": "https://www.databricks.com/product/pricing/foundation-model-serving", + "metadata": {"notes": "Input/output cost per token is dbu cost * $0.070, based on databricks Llama 3.1 70B conversion. Number provided for reference, '*_dbu_cost_per_token' used in actual calculation."}, + "supports_tool_choice": true + + }, "databricks/databricks-meta-llama-3-3-70b-instruct": { "max_tokens": 128000, "max_input_tokens": 128000, From 5dcb87a88bf2ed1bb1ff085c55cfd92ec192e075 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 6 Feb 2025 18:22:08 -0800 Subject: [PATCH 09/74] (bug fix router.py) - safely handle `choices=[]` on llm responses (#8342) * test fix test_router_with_empty_choices * fix _should_raise_content_policy_error --- .../test_router_endpoints.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/router_unit_tests/test_router_endpoints.py b/tests/router_unit_tests/test_router_endpoints.py index 98d8f8f90b..99164827cc 100644 --- a/tests/router_unit_tests/test_router_endpoints.py +++ b/tests/router_unit_tests/test_router_endpoints.py @@ -324,3 +324,28 @@ async def test_anthropic_router_completion_e2e(model_list): AnthropicResponse.model_validate(response) assert response.model == "gpt-3.5-turbo" + + +@pytest.mark.asyncio +async def test_router_with_empty_choices(model_list): + """ + https://github.com/BerriAI/litellm/issues/8306 + """ + router = Router(model_list=model_list) + mock_response = litellm.ModelResponse( + choices=[], + usage=litellm.Usage( + prompt_tokens=10, + completion_tokens=10, + total_tokens=20, + ), + model="gpt-3.5-turbo", + object="chat.completion", + created=1723081200, + ).model_dump() + response = await router.acompletion( + model="gpt-3.5-turbo", + messages=[{"role": "user", "content": "Hello, how are you?"}], + mock_response=mock_response, + ) + assert response is not None From 65c91cbbbc62d38d7cfed2b9d285c645ddaf304c Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 6 Feb 2025 18:27:54 -0800 Subject: [PATCH 10/74] (QA+UI) - e2e flow for adding assembly ai passthrough endpoints (#8337) * add initial test for assembly ai * start using PassthroughEndpointRouter * migrate to lllm passthrough endpoints * add assembly ai as a known provider * fix PassthroughEndpointRouter * fix set_pass_through_credentials * working EU request to assembly ai pass through endpoint * add e2e test assembly * test_assemblyai_routes_with_bad_api_key * clean up pass through endpoint router * e2e testing for assembly ai pass through * test assembly ai e2e testing * delete assembly ai models * fix code quality * ui working assembly ai api base flow * fix install assembly ai * update model call details with kwargs for pass through logging * fix tracking assembly ai model in response * _handle_assemblyai_passthrough_logging * fix test_initialize_deployment_for_pass_through_unsupported_provider * TestPassthroughEndpointRouter * _get_assembly_transcript * fix assembly ai pt logging tests * fix assemblyai_proxy_route * fix _get_assembly_region_from_url --- .circleci/config.yml | 1 + litellm/proxy/_types.py | 1 + .../llm_passthrough_endpoints.py | 54 ++++- .../assembly_passthrough_logging_handler.py | 141 ++++++++++-- .../passthrough_endpoint_router.py | 93 ++++++++ litellm/router.py | 10 +- litellm/types/utils.py | 1 + tests/pass_through_tests/test_assembly_ai.py | 2 +- .../test_assemblyai_unit_tests_passthrough.py | 57 +++-- .../test_unit_test_passthrough_router.py | 134 ++++++++++++ .../test_router_adding_deployments.py | 21 -- .../test_adding_passthrough_model.py | 202 ++++++++++++++++++ .../add_model/provider_specific_fields.tsx | 18 +- 13 files changed, 656 insertions(+), 79 deletions(-) create mode 100644 litellm/proxy/pass_through_endpoints/passthrough_endpoint_router.py create mode 100644 tests/pass_through_unit_tests/test_unit_test_passthrough_router.py create mode 100644 tests/store_model_in_db_tests/test_adding_passthrough_model.py diff --git a/.circleci/config.yml b/.circleci/config.yml index e7e36f1c93..1af15b03a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1552,6 +1552,7 @@ jobs: pip install "pytest-retry==1.6.3" pip install "pytest-mock==3.12.0" pip install "pytest-asyncio==0.21.1" + pip install "assemblyai==0.37.0" - run: name: Build Docker image command: docker build -t my-app:latest -f ./docker/Dockerfile.database . diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index 713925c638..a131e6ce85 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -248,6 +248,7 @@ class LiteLLMRoutes(enum.Enum): "/azure", "/openai", "/assemblyai", + "/eu.assemblyai", ] anthropic_routes = [ diff --git a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py index dce20b9775..3da970234f 100644 --- a/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/llm_passthrough_endpoints.py @@ -20,9 +20,13 @@ from litellm.proxy.pass_through_endpoints.pass_through_endpoints import ( ) from litellm.secret_managers.main import get_secret_str +from .passthrough_endpoint_router import PassthroughEndpointRouter + router = APIRouter() default_vertex_config = None +passthrough_endpoint_router = PassthroughEndpointRouter() + def create_request_copy(request: Request): return { @@ -68,8 +72,9 @@ async def gemini_proxy_route( updated_url = base_url.copy_with(path=encoded_endpoint) # Add or update query parameters - gemini_api_key: Optional[str] = litellm.utils.get_secret( # type: ignore - secret_name="GEMINI_API_KEY" + gemini_api_key: Optional[str] = passthrough_endpoint_router.get_credentials( + custom_llm_provider="gemini", + region_name=None, ) if gemini_api_key is None: raise Exception( @@ -126,7 +131,10 @@ async def cohere_proxy_route( updated_url = base_url.copy_with(path=encoded_endpoint) # Add or update query parameters - cohere_api_key = litellm.utils.get_secret(secret_name="COHERE_API_KEY") + cohere_api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="cohere", + region_name=None, + ) ## check for streaming is_streaming_request = False @@ -175,7 +183,10 @@ async def anthropic_proxy_route( updated_url = base_url.copy_with(path=encoded_endpoint) # Add or update query parameters - anthropic_api_key = litellm.utils.get_secret(secret_name="ANTHROPIC_API_KEY") + anthropic_api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="anthropic", + region_name=None, + ) ## check for streaming is_streaming_request = False @@ -297,18 +308,34 @@ def _is_bedrock_agent_runtime_route(endpoint: str) -> bool: methods=["GET", "POST", "PUT", "DELETE", "PATCH"], tags=["AssemblyAI Pass-through", "pass-through"], ) +@router.api_route( + "/eu.assemblyai/{endpoint:path}", + methods=["GET", "POST", "PUT", "DELETE", "PATCH"], + tags=["AssemblyAI EU Pass-through", "pass-through"], +) async def assemblyai_proxy_route( endpoint: str, request: Request, fastapi_response: Response, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), ): + from litellm.proxy.pass_through_endpoints.llm_provider_handlers.assembly_passthrough_logging_handler import ( + AssemblyAIPassthroughLoggingHandler, + ) + """ [Docs](https://api.assemblyai.com) """ - base_target_url = "https://api.assemblyai.com" + # Set base URL based on the route + assembly_region = AssemblyAIPassthroughLoggingHandler._get_assembly_region_from_url( + url=str(request.url) + ) + base_target_url = ( + AssemblyAIPassthroughLoggingHandler._get_assembly_base_url_from_region( + region=assembly_region + ) + ) encoded_endpoint = httpx.URL(endpoint).path - # Ensure endpoint starts with '/' for proper URL construction if not encoded_endpoint.startswith("/"): encoded_endpoint = "/" + encoded_endpoint @@ -318,7 +345,10 @@ async def assemblyai_proxy_route( updated_url = base_url.copy_with(path=encoded_endpoint) # Add or update query parameters - assemblyai_api_key = litellm.utils.get_secret(secret_name="ASSEMBLYAI_API_KEY") + assemblyai_api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="assemblyai", + region_name=assembly_region, + ) ## check for streaming is_streaming_request = False @@ -366,7 +396,10 @@ async def azure_proxy_route( "Required 'AZURE_API_BASE' in environment to make pass-through calls to Azure." ) # Add or update query parameters - azure_api_key = get_secret_str(secret_name="AZURE_API_KEY") + azure_api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="azure", + region_name=None, + ) if azure_api_key is None: raise Exception( "Required 'AZURE_API_KEY' in environment to make pass-through calls to Azure." @@ -400,7 +433,10 @@ async def openai_proxy_route( """ base_target_url = "https://api.openai.com" # Add or update query parameters - openai_api_key = get_secret_str(secret_name="OPENAI_API_KEY") + openai_api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="openai", + region_name=None, + ) if openai_api_key is None: raise Exception( "Required 'OPENAI_API_KEY' in environment to make pass-through calls to OpenAI." diff --git a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/assembly_passthrough_logging_handler.py b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/assembly_passthrough_logging_handler.py index bf0f0f73a5..2418a435b4 100644 --- a/litellm/proxy/pass_through_endpoints/llm_provider_handlers/assembly_passthrough_logging_handler.py +++ b/litellm/proxy/pass_through_endpoints/llm_provider_handlers/assembly_passthrough_logging_handler.py @@ -1,12 +1,13 @@ import asyncio import json -import os import time from datetime import datetime -from typing import Optional, TypedDict +from typing import Literal, Optional, TypedDict +from urllib.parse import urlparse import httpx +import litellm from litellm._logging import verbose_proxy_logger from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj from litellm.litellm_core_utils.litellm_logging import ( @@ -18,7 +19,7 @@ from litellm.proxy.pass_through_endpoints.types import PassthroughStandardLoggin class AssemblyAITranscriptResponse(TypedDict, total=False): id: str - language_model: str + speech_model: str acoustic_model: str language_code: str status: str @@ -27,7 +28,8 @@ class AssemblyAITranscriptResponse(TypedDict, total=False): class AssemblyAIPassthroughLoggingHandler: def __init__(self): - self.assembly_ai_base_url = "https://api.assemblyai.com/v2" + self.assembly_ai_base_url = "https://api.assemblyai.com" + self.assembly_ai_eu_base_url = "https://eu.assemblyai.com" """ The base URL for the AssemblyAI API """ @@ -43,8 +45,6 @@ class AssemblyAIPassthroughLoggingHandler: The maximum number of polling attempts for the AssemblyAI API. """ - self.assemblyai_api_key = os.environ.get("ASSEMBLYAI_API_KEY") - def assemblyai_passthrough_logging_handler( self, httpx_response: httpx.Response, @@ -90,27 +90,34 @@ class AssemblyAIPassthroughLoggingHandler: """ from ..pass_through_endpoints import pass_through_endpoint_logging - model = response_body.get("model", "") - verbose_proxy_logger.debug("response body", json.dumps(response_body, indent=4)) + model = response_body.get("speech_model", "") + verbose_proxy_logger.debug( + "response body %s", json.dumps(response_body, indent=4) + ) kwargs["model"] = model kwargs["custom_llm_provider"] = "assemblyai" + logging_obj.model_call_details["model"] = model + logging_obj.model_call_details["custom_llm_provider"] = "assemblyai" transcript_id = response_body.get("id") if transcript_id is None: raise ValueError( "Transcript ID is required to log the cost of the transcription" ) - transcript_response = self._poll_assembly_for_transcript_response(transcript_id) + transcript_response = self._poll_assembly_for_transcript_response( + transcript_id=transcript_id, url_route=url_route + ) verbose_proxy_logger.debug( - "finished polling assembly for transcript response- got transcript response", + "finished polling assembly for transcript response- got transcript response %s", json.dumps(transcript_response, indent=4), ) if transcript_response: - cost = self.get_cost_for_assembly_transcript(transcript_response) + cost = self.get_cost_for_assembly_transcript( + speech_model=model, + transcript_response=transcript_response, + ) kwargs["response_cost"] = cost - logging_obj.model_call_details["model"] = logging_obj.model - # Make standard logging object for Vertex AI standard_logging_object = get_standard_logging_object_payload( kwargs=kwargs, @@ -157,7 +164,11 @@ class AssemblyAIPassthroughLoggingHandler: return {} return dict(transcript_response) - def _get_assembly_transcript(self, transcript_id: str) -> Optional[dict]: + def _get_assembly_transcript( + self, + transcript_id: str, + request_region: Optional[Literal["eu"]] = None, + ) -> Optional[dict]: """ Get the transcript details from AssemblyAI API @@ -167,10 +178,25 @@ class AssemblyAIPassthroughLoggingHandler: Returns: Optional[dict]: Transcript details if successful, None otherwise """ + from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import ( + passthrough_endpoint_router, + ) + + _base_url = ( + self.assembly_ai_eu_base_url + if request_region == "eu" + else self.assembly_ai_base_url + ) + _api_key = passthrough_endpoint_router.get_credentials( + custom_llm_provider="assemblyai", + region_name=request_region, + ) + if _api_key is None: + raise ValueError("AssemblyAI API key not found") try: - url = f"{self.assembly_ai_base_url}/transcript/{transcript_id}" + url = f"{_base_url}/v2/transcript/{transcript_id}" headers = { - "Authorization": f"Bearer {self.assemblyai_api_key}", + "Authorization": f"Bearer {_api_key}", "Content-Type": "application/json", } @@ -179,11 +205,15 @@ class AssemblyAIPassthroughLoggingHandler: return response.json() except Exception as e: - verbose_proxy_logger.debug(f"Error getting AssemblyAI transcript: {str(e)}") + verbose_proxy_logger.exception( + f"[Non blocking logging error] Error getting AssemblyAI transcript: {str(e)}" + ) return None def _poll_assembly_for_transcript_response( - self, transcript_id: str + self, + transcript_id: str, + url_route: Optional[str] = None, ) -> Optional[AssemblyAITranscriptResponse]: """ Poll the status of the transcript until it is completed or timeout (30 minutes) @@ -191,7 +221,12 @@ class AssemblyAIPassthroughLoggingHandler: for _ in range( self.max_polling_attempts ): # 180 attempts * 10s = 30 minutes max - transcript = self._get_assembly_transcript(transcript_id) + transcript = self._get_assembly_transcript( + request_region=AssemblyAIPassthroughLoggingHandler._get_assembly_region_from_url( + url=url_route + ), + transcript_id=transcript_id, + ) if transcript is None: return None if ( @@ -205,6 +240,7 @@ class AssemblyAIPassthroughLoggingHandler: @staticmethod def get_cost_for_assembly_transcript( transcript_response: AssemblyAITranscriptResponse, + speech_model: str, ) -> Optional[float]: """ Get the cost for the assembly transcript @@ -212,7 +248,50 @@ class AssemblyAIPassthroughLoggingHandler: _audio_duration = transcript_response.get("audio_duration") if _audio_duration is None: return None - return _audio_duration * 0.0001 + _cost_per_second = ( + AssemblyAIPassthroughLoggingHandler.get_cost_per_second_for_assembly_model( + speech_model=speech_model + ) + ) + if _cost_per_second is None: + return None + return _audio_duration * _cost_per_second + + @staticmethod + def get_cost_per_second_for_assembly_model(speech_model: str) -> Optional[float]: + """ + Get the cost per second for the assembly model. + Falls back to assemblyai/nano if the specific speech model info cannot be found. + """ + try: + # First try with the provided speech model + try: + model_info = litellm.get_model_info( + model=speech_model, + custom_llm_provider="assemblyai", + ) + if model_info and model_info.get("input_cost_per_second") is not None: + return model_info.get("input_cost_per_second") + except Exception: + pass # Continue to fallback if model not found + + # Fallback to assemblyai/nano if speech model info not found + try: + model_info = litellm.get_model_info( + model="assemblyai/nano", + custom_llm_provider="assemblyai", + ) + if model_info and model_info.get("input_cost_per_second") is not None: + return model_info.get("input_cost_per_second") + except Exception: + pass + + return None + except Exception as e: + verbose_proxy_logger.exception( + f"[Non blocking logging error] Error getting AssemblyAI model info: {str(e)}" + ) + return None @staticmethod def _should_log_request(request_method: str) -> bool: @@ -220,3 +299,25 @@ class AssemblyAIPassthroughLoggingHandler: only POST transcription jobs are logged. litellm will POLL assembly to wait for the transcription to complete to log the complete response / cost """ return request_method == "POST" + + @staticmethod + def _get_assembly_region_from_url(url: Optional[str]) -> Optional[Literal["eu"]]: + """ + Get the region from the URL + """ + if url is None: + return None + if urlparse(url).hostname == "eu.assemblyai.com": + return "eu" + return None + + @staticmethod + def _get_assembly_base_url_from_region(region: Optional[Literal["eu"]]) -> str: + """ + Get the base URL for the AssemblyAI API + if region == "eu", return "https://api.eu.assemblyai.com" + else return "https://api.assemblyai.com" + """ + if region == "eu": + return "https://api.eu.assemblyai.com" + return "https://api.assemblyai.com" diff --git a/litellm/proxy/pass_through_endpoints/passthrough_endpoint_router.py b/litellm/proxy/pass_through_endpoints/passthrough_endpoint_router.py new file mode 100644 index 0000000000..adf7d0f30c --- /dev/null +++ b/litellm/proxy/pass_through_endpoints/passthrough_endpoint_router.py @@ -0,0 +1,93 @@ +from typing import Dict, Optional + +from litellm._logging import verbose_logger +from litellm.secret_managers.main import get_secret_str + + +class PassthroughEndpointRouter: + """ + Use this class to Set/Get credentials for pass-through endpoints + """ + + def __init__(self): + self.credentials: Dict[str, str] = {} + + def set_pass_through_credentials( + self, + custom_llm_provider: str, + api_base: Optional[str], + api_key: Optional[str], + ): + """ + Set credentials for a pass-through endpoint. Used when a user adds a pass-through LLM endpoint on the UI. + + Args: + custom_llm_provider: The provider of the pass-through endpoint + api_base: The base URL of the pass-through endpoint + api_key: The API key for the pass-through endpoint + """ + credential_name = self._get_credential_name_for_provider( + custom_llm_provider=custom_llm_provider, + region_name=self._get_region_name_from_api_base( + api_base=api_base, custom_llm_provider=custom_llm_provider + ), + ) + if api_key is None: + raise ValueError("api_key is required for setting pass-through credentials") + self.credentials[credential_name] = api_key + + def get_credentials( + self, + custom_llm_provider: str, + region_name: Optional[str], + ) -> Optional[str]: + credential_name = self._get_credential_name_for_provider( + custom_llm_provider=custom_llm_provider, + region_name=region_name, + ) + verbose_logger.debug( + f"Pass-through llm endpoints router, looking for credentials for {credential_name}" + ) + if credential_name in self.credentials: + verbose_logger.debug(f"Found credentials for {credential_name}") + return self.credentials[credential_name] + else: + verbose_logger.debug( + f"No credentials found for {credential_name}, looking for env variable" + ) + _env_variable_name = ( + self._get_default_env_variable_name_passthrough_endpoint( + custom_llm_provider=custom_llm_provider, + ) + ) + return get_secret_str(_env_variable_name) + + def _get_credential_name_for_provider( + self, + custom_llm_provider: str, + region_name: Optional[str], + ) -> str: + if region_name is None: + return f"{custom_llm_provider.upper()}_API_KEY" + return f"{custom_llm_provider.upper()}_{region_name.upper()}_API_KEY" + + def _get_region_name_from_api_base( + self, + custom_llm_provider: str, + api_base: Optional[str], + ) -> Optional[str]: + """ + Get the region name from the API base. + + Each provider might have a different way of specifying the region in the API base - this is where you can use conditional logic to handle that. + """ + if custom_llm_provider == "assemblyai": + if api_base and "eu" in api_base: + return "eu" + return None + + @staticmethod + def _get_default_env_variable_name_passthrough_endpoint( + custom_llm_provider: str, + ) -> str: + return f"{custom_llm_provider.upper()}_API_KEY" diff --git a/litellm/router.py b/litellm/router.py index b61c30dd57..6d6a92d3b1 100644 --- a/litellm/router.py +++ b/litellm/router.py @@ -4180,8 +4180,14 @@ class Router: vertex_credentials=deployment.litellm_params.vertex_credentials, ) else: - verbose_router_logger.error( - f"Unsupported provider - {custom_llm_provider} for pass-through endpoints" + from litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints import ( + passthrough_endpoint_router, + ) + + passthrough_endpoint_router.set_pass_through_credentials( + custom_llm_provider=custom_llm_provider, + api_base=deployment.litellm_params.api_base, + api_key=deployment.litellm_params.api_key, ) pass pass diff --git a/litellm/types/utils.py b/litellm/types/utils.py index a2e5448dd1..76d7f008bb 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -1870,6 +1870,7 @@ class LlmProviders(str, Enum): LANGFUSE = "langfuse" HUMANLOOP = "humanloop" TOPAZ = "topaz" + ASSEMBLYAI = "assemblyai" # Create a set of all provider values for quick lookup diff --git a/tests/pass_through_tests/test_assembly_ai.py b/tests/pass_through_tests/test_assembly_ai.py index dd9f91a0b4..2d01ef2c1b 100644 --- a/tests/pass_through_tests/test_assembly_ai.py +++ b/tests/pass_through_tests/test_assembly_ai.py @@ -1,5 +1,5 @@ """ -This test ensures that the proxy can passthrough anthropic requests +This test ensures that the proxy can passthrough requests to assemblyai """ import pytest diff --git a/tests/pass_through_unit_tests/test_assemblyai_unit_tests_passthrough.py b/tests/pass_through_unit_tests/test_assemblyai_unit_tests_passthrough.py index 8279d4c988..963f1ad6ef 100644 --- a/tests/pass_through_unit_tests/test_assemblyai_unit_tests_passthrough.py +++ b/tests/pass_through_unit_tests/test_assemblyai_unit_tests_passthrough.py @@ -41,7 +41,6 @@ from litellm.proxy.pass_through_endpoints.success_handler import ( @pytest.fixture def assembly_handler(): handler = AssemblyAIPassthroughLoggingHandler() - handler.assemblyai_api_key = "test-key" return handler @@ -66,21 +65,27 @@ def test_should_log_request(): def test_get_assembly_transcript(assembly_handler, mock_transcript_response): """ Test that the _get_assembly_transcript method calls GET /v2/transcript/{transcript_id} + and uses the test key returned by the mocked get_credentials. """ - with patch("httpx.get") as mock_get: - mock_get.return_value.json.return_value = mock_transcript_response - mock_get.return_value.raise_for_status.return_value = None + # Patch get_credentials to return "test-key" + with patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials", + return_value="test-key", + ): + with patch("httpx.get") as mock_get: + mock_get.return_value.json.return_value = mock_transcript_response + mock_get.return_value.raise_for_status.return_value = None - transcript = assembly_handler._get_assembly_transcript("test-transcript-id") - assert transcript == mock_transcript_response + transcript = assembly_handler._get_assembly_transcript("test-transcript-id") + assert transcript == mock_transcript_response - mock_get.assert_called_once_with( - "https://api.assemblyai.com/v2/transcript/test-transcript-id", - headers={ - "Authorization": "Bearer test-key", - "Content-Type": "application/json", - }, - ) + mock_get.assert_called_once_with( + "https://api.assemblyai.com/v2/transcript/test-transcript-id", + headers={ + "Authorization": "Bearer test-key", + "Content-Type": "application/json", + }, + ) def test_poll_assembly_for_transcript_response( @@ -89,18 +94,24 @@ def test_poll_assembly_for_transcript_response( """ Test that the _poll_assembly_for_transcript_response method returns the correct transcript response """ - with patch("httpx.get") as mock_get: - mock_get.return_value.json.return_value = mock_transcript_response - mock_get.return_value.raise_for_status.return_value = None + with patch( + "litellm.proxy.pass_through_endpoints.llm_passthrough_endpoints.passthrough_endpoint_router.get_credentials", + return_value="test-key", + ): + with patch("httpx.get") as mock_get: + mock_get.return_value.json.return_value = mock_transcript_response + mock_get.return_value.raise_for_status.return_value = None - # Override polling settings for faster test - assembly_handler.polling_interval = 0.01 - assembly_handler.max_polling_attempts = 2 + # Override polling settings for faster test + assembly_handler.polling_interval = 0.01 + assembly_handler.max_polling_attempts = 2 - transcript = assembly_handler._poll_assembly_for_transcript_response( - "test-transcript-id" - ) - assert transcript == AssemblyAITranscriptResponse(**mock_transcript_response) + transcript = assembly_handler._poll_assembly_for_transcript_response( + "test-transcript-id", + ) + assert transcript == AssemblyAITranscriptResponse( + **mock_transcript_response + ) def test_is_assemblyai_route(): diff --git a/tests/pass_through_unit_tests/test_unit_test_passthrough_router.py b/tests/pass_through_unit_tests/test_unit_test_passthrough_router.py new file mode 100644 index 0000000000..6e8296876a --- /dev/null +++ b/tests/pass_through_unit_tests/test_unit_test_passthrough_router.py @@ -0,0 +1,134 @@ +import json +import os +import sys +from datetime import datetime +from unittest.mock import AsyncMock, Mock, patch, MagicMock + +sys.path.insert(0, os.path.abspath("../..")) # + +import unittest +from unittest.mock import patch +from litellm.proxy.pass_through_endpoints.passthrough_endpoint_router import ( + PassthroughEndpointRouter, +) + +passthrough_endpoint_router = PassthroughEndpointRouter() + +""" +1. Basic Usage + - Set OpenAI, AssemblyAI, Anthropic, Cohere credentials + - GET credentials from passthrough_endpoint_router + +2. Basic Usage - when not using DB +- No credentials set +- call GET credentials with provider name, assert that it reads the secret from the environment variable + + +3. Unit test for _get_default_env_variable_name_passthrough_endpoint +""" + + +class TestPassthroughEndpointRouter(unittest.TestCase): + def setUp(self): + self.router = PassthroughEndpointRouter() + + def test_set_and_get_credentials(self): + """ + 1. Basic Usage: + - Set credentials for OpenAI, AssemblyAI, Anthropic, Cohere + - GET credentials from passthrough_endpoint_router (from the memory store when available) + """ + + # OpenAI: standard (no region-specific logic) + self.router.set_pass_through_credentials("openai", None, "openai_key") + self.assertEqual(self.router.get_credentials("openai", None), "openai_key") + + # AssemblyAI: using an API base that contains 'eu' should trigger regional logic. + api_base_eu = "https://api.eu.assemblyai.com" + self.router.set_pass_through_credentials( + "assemblyai", api_base_eu, "assemblyai_key" + ) + # When calling get_credentials, pass the region "eu" (extracted from the API base) + self.assertEqual( + self.router.get_credentials("assemblyai", "eu"), "assemblyai_key" + ) + + # Anthropic: no region set + self.router.set_pass_through_credentials("anthropic", None, "anthropic_key") + self.assertEqual( + self.router.get_credentials("anthropic", None), "anthropic_key" + ) + + # Cohere: no region set + self.router.set_pass_through_credentials("cohere", None, "cohere_key") + self.assertEqual(self.router.get_credentials("cohere", None), "cohere_key") + + def test_get_credentials_from_env(self): + """ + 2. Basic Usage - when not using the database: + - No credentials set in memory + - Call get_credentials with provider name and expect it to read from the environment variable (via get_secret_str) + """ + # Patch the get_secret_str function within the router's module. + with patch( + "litellm.proxy.pass_through_endpoints.passthrough_endpoint_router.get_secret_str" + ) as mock_get_secret: + mock_get_secret.return_value = "env_openai_key" + # For "openai", if credentials are not set, it should fallback to the env variable. + result = self.router.get_credentials("openai", None) + self.assertEqual(result, "env_openai_key") + mock_get_secret.assert_called_once_with("OPENAI_API_KEY") + + with patch( + "litellm.proxy.pass_through_endpoints.passthrough_endpoint_router.get_secret_str" + ) as mock_get_secret: + mock_get_secret.return_value = "env_cohere_key" + result = self.router.get_credentials("cohere", None) + self.assertEqual(result, "env_cohere_key") + mock_get_secret.assert_called_once_with("COHERE_API_KEY") + + with patch( + "litellm.proxy.pass_through_endpoints.passthrough_endpoint_router.get_secret_str" + ) as mock_get_secret: + mock_get_secret.return_value = "env_anthropic_key" + result = self.router.get_credentials("anthropic", None) + self.assertEqual(result, "env_anthropic_key") + mock_get_secret.assert_called_once_with("ANTHROPIC_API_KEY") + + with patch( + "litellm.proxy.pass_through_endpoints.passthrough_endpoint_router.get_secret_str" + ) as mock_get_secret: + mock_get_secret.return_value = "env_azure_key" + result = self.router.get_credentials("azure", None) + self.assertEqual(result, "env_azure_key") + mock_get_secret.assert_called_once_with("AZURE_API_KEY") + + def test_default_env_variable_method(self): + """ + 3. Unit test for _get_default_env_variable_name_passthrough_endpoint: + - Should return the provider in uppercase followed by _API_KEY. + """ + self.assertEqual( + PassthroughEndpointRouter._get_default_env_variable_name_passthrough_endpoint( + "openai" + ), + "OPENAI_API_KEY", + ) + self.assertEqual( + PassthroughEndpointRouter._get_default_env_variable_name_passthrough_endpoint( + "assemblyai" + ), + "ASSEMBLYAI_API_KEY", + ) + self.assertEqual( + PassthroughEndpointRouter._get_default_env_variable_name_passthrough_endpoint( + "anthropic" + ), + "ANTHROPIC_API_KEY", + ) + self.assertEqual( + PassthroughEndpointRouter._get_default_env_variable_name_passthrough_endpoint( + "cohere" + ), + "COHERE_API_KEY", + ) diff --git a/tests/router_unit_tests/test_router_adding_deployments.py b/tests/router_unit_tests/test_router_adding_deployments.py index b5e2d4a526..fca3f147e5 100644 --- a/tests/router_unit_tests/test_router_adding_deployments.py +++ b/tests/router_unit_tests/test_router_adding_deployments.py @@ -76,27 +76,6 @@ def test_initialize_deployment_for_pass_through_missing_params(): ) -def test_initialize_deployment_for_pass_through_unsupported_provider(): - """ - Test initialization with an unsupported provider - """ - router = Router(model_list=[]) - deployment = Deployment( - model_name="unsupported-test", - litellm_params=LiteLLM_Params( - model="unsupported/test-model", - use_in_pass_through=True, - ), - ) - - # Should not raise an error, but log a warning - router._initialize_deployment_for_pass_through( - deployment=deployment, - custom_llm_provider="unsupported_provider", - model="unsupported/test-model", - ) - - def test_initialize_deployment_when_pass_through_disabled(): """ Test that initialization simply exits when use_in_pass_through is False diff --git a/tests/store_model_in_db_tests/test_adding_passthrough_model.py b/tests/store_model_in_db_tests/test_adding_passthrough_model.py new file mode 100644 index 0000000000..ad26e19bd6 --- /dev/null +++ b/tests/store_model_in_db_tests/test_adding_passthrough_model.py @@ -0,0 +1,202 @@ +""" +Test adding a pass through assemblyai model + api key + api base to the db +wait 20 seconds +make request + +Cases to cover +1. user points api base to /assemblyai +2. user points api base to /asssemblyai/us +3. user points api base to /assemblyai/eu +4. Bad API Key / credential - 401 +""" + +import time +import assemblyai as aai +import pytest +import httpx +import os +import json + +TEST_MASTER_KEY = "sk-1234" +PROXY_BASE_URL = "http://0.0.0.0:4000" +US_BASE_URL = f"{PROXY_BASE_URL}/assemblyai" +EU_BASE_URL = f"{PROXY_BASE_URL}/eu.assemblyai" +ASSEMBLYAI_API_KEY_ENV_VAR = "TEST_SPECIAL_ASSEMBLYAI_API_KEY" + + +def _delete_all_assemblyai_models_from_db(): + """ + Delete all assemblyai models from the db + """ + print("Deleting all assemblyai models from the db.......") + model_list_response = httpx.get( + url=f"{PROXY_BASE_URL}/v2/model/info", + headers={"Authorization": f"Bearer {TEST_MASTER_KEY}"}, + ) + response_data = model_list_response.json() + print("model list response", json.dumps(response_data, indent=4, default=str)) + # Filter for only AssemblyAI models + assemblyai_models = [ + model + for model in response_data["data"] + if model.get("litellm_params", {}).get("custom_llm_provider") == "assemblyai" + ] + + for model in assemblyai_models: + model_id = model["model_info"]["id"] + httpx.post( + url=f"{PROXY_BASE_URL}/model/delete", + headers={"Authorization": f"Bearer {TEST_MASTER_KEY}"}, + json={"id": model_id}, + ) + print("Deleted all assemblyai models from the db") + + +@pytest.fixture(autouse=True) +def cleanup_assemblyai_models(): + """ + Fixture to clean up AssemblyAI models before and after each test + """ + # Clean up before test + _delete_all_assemblyai_models_from_db() + + # Run the test + yield + + # Clean up after test + _delete_all_assemblyai_models_from_db() + + +def test_e2e_assemblyai_passthrough(): + """ + Test adding a pass through assemblyai model + api key + api base to the db + wait 20 seconds + make request + """ + add_assembly_ai_model_to_db(api_base="https://api.assemblyai.com") + virtual_key = create_virtual_key() + # make request + make_assemblyai_basic_transcribe_request( + virtual_key=virtual_key, assemblyai_base_url=US_BASE_URL + ) + + pass + + +def test_e2e_assemblyai_passthrough_eu(): + """ + Test adding a pass through assemblyai model + api key + api base to the db + wait 20 seconds + make request + """ + add_assembly_ai_model_to_db(api_base="https://api.eu.assemblyai.com") + virtual_key = create_virtual_key() + # make request + make_assemblyai_basic_transcribe_request( + virtual_key=virtual_key, assemblyai_base_url=EU_BASE_URL + ) + + pass + + +def test_assemblyai_routes_with_bad_api_key(): + """ + Test AssemblyAI endpoints with invalid API key to ensure proper error handling + """ + bad_api_key = "sk-12222" + payload = { + "audio_url": "https://assembly.ai/wildfires.mp3", + "audio_end_at": 280, + "audio_start_from": 10, + "auto_chapters": True, + } + headers = { + "Authorization": f"Bearer {bad_api_key}", + "Content-Type": "application/json", + } + + # Test EU endpoint + eu_response = httpx.post( + f"{PROXY_BASE_URL}/eu.assemblyai/v2/transcript", headers=headers, json=payload + ) + assert ( + eu_response.status_code == 401 + ), f"Expected 401 unauthorized, got {eu_response.status_code}" + + # Test US endpoint + us_response = httpx.post( + f"{PROXY_BASE_URL}/assemblyai/v2/transcript", headers=headers, json=payload + ) + assert ( + us_response.status_code == 401 + ), f"Expected 401 unauthorized, got {us_response.status_code}" + + +def create_virtual_key(): + """ + Create a virtual key + """ + response = httpx.post( + url=f"{PROXY_BASE_URL}/key/generate", + headers={"Authorization": f"Bearer {TEST_MASTER_KEY}"}, + json={}, + ) + print(response.json()) + return response.json()["token"] + + +def add_assembly_ai_model_to_db( + api_base: str, +): + """ + Add the assemblyai model to the db - makes a http request to the /model/new endpoint on PROXY_BASE_URL + """ + print("assmbly ai api key", os.getenv(ASSEMBLYAI_API_KEY_ENV_VAR)) + response = httpx.post( + url=f"{PROXY_BASE_URL}/model/new", + headers={"Authorization": f"Bearer {TEST_MASTER_KEY}"}, + json={ + "model_name": "assemblyai/*", + "litellm_params": { + "model": "assemblyai/*", + "custom_llm_provider": "assemblyai", + "api_key": os.getenv(ASSEMBLYAI_API_KEY_ENV_VAR), + "api_base": api_base, + "use_in_pass_through": True, + }, + "model_info": {}, + }, + ) + print(response.json()) + pass + + +def make_assemblyai_basic_transcribe_request( + virtual_key: str, assemblyai_base_url: str +): + print("making basic transcribe request to assemblyai passthrough") + + # Replace with your API key + aai.settings.api_key = f"Bearer {virtual_key}" + aai.settings.base_url = assemblyai_base_url + + # URL of the file to transcribe + FILE_URL = "https://assembly.ai/wildfires.mp3" + + # You can also transcribe a local file by passing in a file path + # FILE_URL = './path/to/file.mp3' + + transcriber = aai.Transcriber() + transcript = transcriber.transcribe(FILE_URL) + print(transcript) + print(transcript.id) + if transcript.id: + transcript.delete_by_id(transcript.id) + else: + pytest.fail("Failed to get transcript id") + + if transcript.status == aai.TranscriptStatus.error: + print(transcript.error) + pytest.fail(f"Failed to transcribe file error: {transcript.error}") + else: + print(transcript.text) diff --git a/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.tsx b/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.tsx index 38f7edea90..f48763a6f4 100644 --- a/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.tsx +++ b/ui/litellm-dashboard/src/components/add_model/provider_specific_fields.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Form } from "antd"; +import { Form, Select } from "antd"; import { TextInput, Text } from "@tremor/react"; import { Row, Col, Typography, Button as Button2, Upload, UploadProps } from "antd"; import { UploadOutlined } from "@ant-design/icons"; @@ -72,9 +72,21 @@ const ProviderSpecificFields: React.FC = ({ )} + {selectedProviderEnum === Providers.AssemblyAI && ( + + + + )} + {(selectedProviderEnum === Providers.Azure || - selectedProviderEnum === Providers.OpenAI_Compatible || - selectedProviderEnum === Providers.AssemblyAI + selectedProviderEnum === Providers.OpenAI_Compatible ) && ( Date: Thu, 6 Feb 2025 18:31:21 -0800 Subject: [PATCH 11/74] ui new build --- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/250-27e715296ed15b72.js | 1 + .../out/_next/static/chunks/250-57462126852c026f.js | 1 - .../out/_next/static/chunks/261-c5b2a5c7eb59699f.js | 2 +- .../{309-a4a536a89a6eb4e7.js => 309-693d6937980786e0.js} | 2 +- .../out/_next/static/chunks/app/page-4e4a5c785ab5bb00.js | 1 + .../out/_next/static/chunks/app/page-cce31e700e2d30b6.js | 1 - litellm/proxy/_experimental/out/index.html | 2 +- litellm/proxy/_experimental/out/index.txt | 4 ++-- litellm/proxy/_experimental/out/model_hub.txt | 4 ++-- litellm/proxy/_experimental/out/onboarding.html | 1 + litellm/proxy/_experimental/out/onboarding.txt | 4 ++-- ui/litellm-dashboard/out/404.html | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 .../out/_next/static/chunks/250-27e715296ed15b72.js | 1 + .../out/_next/static/chunks/250-57462126852c026f.js | 1 - .../out/_next/static/chunks/261-c5b2a5c7eb59699f.js | 2 +- .../{309-a4a536a89a6eb4e7.js => 309-693d6937980786e0.js} | 2 +- .../out/_next/static/chunks/app/page-4e4a5c785ab5bb00.js | 1 + .../out/_next/static/chunks/app/page-cce31e700e2d30b6.js | 1 - ui/litellm-dashboard/out/index.html | 2 +- ui/litellm-dashboard/out/index.txt | 4 ++-- ui/litellm-dashboard/out/model_hub.html | 2 +- ui/litellm-dashboard/out/model_hub.txt | 4 ++-- ui/litellm-dashboard/out/onboarding.html | 2 +- ui/litellm-dashboard/out/onboarding.txt | 4 ++-- 28 files changed, 26 insertions(+), 25 deletions(-) rename litellm/proxy/_experimental/out/_next/static/{0hdHMe2pYzKXDJjNw5w42 => IbKB4Nl3noCPXPBNphSPf}/_buildManifest.js (100%) rename litellm/proxy/_experimental/out/_next/static/{0hdHMe2pYzKXDJjNw5w42 => IbKB4Nl3noCPXPBNphSPf}/_ssgManifest.js (100%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/250-27e715296ed15b72.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/250-57462126852c026f.js rename ui/litellm-dashboard/out/_next/static/chunks/261-45feb99696985c63.js => litellm/proxy/_experimental/out/_next/static/chunks/261-c5b2a5c7eb59699f.js (99%) rename litellm/proxy/_experimental/out/_next/static/chunks/{309-a4a536a89a6eb4e7.js => 309-693d6937980786e0.js} (63%) create mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-4e4a5c785ab5bb00.js delete mode 100644 litellm/proxy/_experimental/out/_next/static/chunks/app/page-cce31e700e2d30b6.js create mode 100644 litellm/proxy/_experimental/out/onboarding.html rename ui/litellm-dashboard/out/_next/static/{0hdHMe2pYzKXDJjNw5w42 => IbKB4Nl3noCPXPBNphSPf}/_buildManifest.js (100%) rename ui/litellm-dashboard/out/_next/static/{0hdHMe2pYzKXDJjNw5w42 => IbKB4Nl3noCPXPBNphSPf}/_ssgManifest.js (100%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/250-27e715296ed15b72.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/250-57462126852c026f.js rename litellm/proxy/_experimental/out/_next/static/chunks/261-45feb99696985c63.js => ui/litellm-dashboard/out/_next/static/chunks/261-c5b2a5c7eb59699f.js (99%) rename ui/litellm-dashboard/out/_next/static/chunks/{309-a4a536a89a6eb4e7.js => 309-693d6937980786e0.js} (63%) create mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-4e4a5c785ab5bb00.js delete mode 100644 ui/litellm-dashboard/out/_next/static/chunks/app/page-cce31e700e2d30b6.js diff --git a/litellm/proxy/_experimental/out/_next/static/0hdHMe2pYzKXDJjNw5w42/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/IbKB4Nl3noCPXPBNphSPf/_buildManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/0hdHMe2pYzKXDJjNw5w42/_buildManifest.js rename to litellm/proxy/_experimental/out/_next/static/IbKB4Nl3noCPXPBNphSPf/_buildManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/0hdHMe2pYzKXDJjNw5w42/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/IbKB4Nl3noCPXPBNphSPf/_ssgManifest.js similarity index 100% rename from litellm/proxy/_experimental/out/_next/static/0hdHMe2pYzKXDJjNw5w42/_ssgManifest.js rename to litellm/proxy/_experimental/out/_next/static/IbKB4Nl3noCPXPBNphSPf/_ssgManifest.js diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-27e715296ed15b72.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-27e715296ed15b72.js new file mode 100644 index 0000000000..b9c75b1d47 --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/250-27e715296ed15b72.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[250],{19250:function(e,t,o){o.d(t,{$I:function(){return R},AZ:function(){return G},Au:function(){return en},BL:function(){return eg},Br:function(){return _},E9:function(){return ej},EG:function(){return eF},EY:function(){return ex},Eb:function(){return E},FC:function(){return $},Gh:function(){return ed},H1:function(){return x},I1:function(){return T},It:function(){return C},J$:function(){return q},K8:function(){return l},K_:function(){return eb},Lp:function(){return eu},N8:function(){return z},NV:function(){return p},Nc:function(){return ei},O3:function(){return em},OU:function(){return W},Og:function(){return h},Ov:function(){return g},PT:function(){return J},RQ:function(){return k},Rg:function(){return U},So:function(){return Z},Vt:function(){return e_},W_:function(){return S},X:function(){return D},XO:function(){return f},Xd:function(){return es},Xm:function(){return N},YU:function(){return eT},Zr:function(){return u},a6:function(){return F},ao:function(){return eC},b1:function(){return Q},cu:function(){return eh},eH:function(){return I},fP:function(){return L},g:function(){return eS},h3:function(){return K},hT:function(){return el},hy:function(){return w},j2:function(){return H},jA:function(){return eN},jE:function(){return ek},kK:function(){return d},kn:function(){return A},lg:function(){return ec},mR:function(){return M},m_:function(){return B},mp:function(){return eE},n$:function(){return er},o6:function(){return V},pf:function(){return ef},qI:function(){return y},qm:function(){return i},r6:function(){return b},rs:function(){return j},s0:function(){return O},sN:function(){return ep},t3:function(){return eB},tN:function(){return Y},u5:function(){return X},um:function(){return ew},v9:function(){return ea},vh:function(){return ey},wX:function(){return m},wd:function(){return ee},xA:function(){return eo},zg:function(){return et}});var r=o(41021);console.log=function(){};let a=0,n=e=>new Promise(t=>setTimeout(t,e)),s=async e=>{let t=Date.now();t-a>6e4?(e.includes("Authentication Error - Expired Key")&&(r.ZP.info("UI Session Expired. Logging out."),a=t,await n(3e3),document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href="/"),a=t):console.log("Error suppressed to prevent spam:",e)},c="Authorization";function l(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),c=e}let i=async e=>{try{let t=await fetch("/get/litellm_model_cost_map",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}}),o=await t.json();return console.log("received litellm model cost data: ".concat(o)),o}catch(e){throw console.error("Failed to get model cost map:",e),e}},d=async(e,t)=>{try{let o=await fetch("/model/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await o.json();return console.log("API Response:",a),r.ZP.success("Model created successfully"),a}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t=await fetch("/model/settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},h=async(e,t)=>{console.log("model_id in model delete call: ".concat(t));try{let o=await fetch("/model/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=await fetch("/budget/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=await fetch("/budget/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=await fetch("/budget/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,t)=>{try{let o=await fetch("/invitation/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=await fetch("/alerting/settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},m=async(e,t,o)=>{try{if(console.log("Form Values in keyCreateCall:",o),o.description&&(o.metadata||(o.metadata={}),o.metadata.description=o.description,delete o.description,o.metadata=JSON.stringify(o.metadata)),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",o);let r=await fetch("/key/generate",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t,o)=>{try{if(console.log("Form Values in keyCreateCall:",o),o.description&&(o.metadata||(o.metadata={}),o.metadata.description=o.description,delete o.description,o.metadata=JSON.stringify(o.metadata)),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",o);let r=await fetch("/user/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t)=>{try{console.log("in keyDeleteCall:",t);let o=await fetch("/key/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{console.log("in userDeleteCall:",t);let o=await fetch("/user/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to delete user(s):",e),e}},j=async(e,t)=>{try{console.log("in teamDeleteCall:",t);let o=await fetch("/team/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to delete key:",e),e}},_=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,n=arguments.length>5?arguments[5]:void 0;try{let l;if(r){l="/user/list";let e=new URLSearchParams;null!=a&&e.append("page",a.toString()),null!=n&&e.append("page_size",n.toString()),l+="?".concat(e.toString())}else l="/user/info","Admin"===o||"Admin Viewer"===o||t&&(l+="?user_id=".concat(t));console.log("Requesting user data from:",l);let i=await fetch(l,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!i.ok){let e=await i.text();throw s(e),Error("Network response was not ok")}let d=await i.json();return console.log("API Response:",d),d}catch(e){throw console.error("Failed to fetch user data:",e),e}},N=async(e,t)=>{try{let o="/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},C=async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{let o="/team/list";console.log("in teamInfoCall"),t&&(o+="?user_id=".concat(t));let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("/team/list API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},F=async e=>{try{console.log("in availableTeamListCall");let t=await fetch("/team/available",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log("/team/available_teams API Response:",o),o}catch(e){throw e}},b=async e=>{try{let t=await fetch("/organization/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{console.log("Form Values in organizationCreateCall:",t);let o=await fetch("/organization/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},S=async e=>{try{let t="/onboarding/get_token";t+="?invite_link=".concat(e);let o=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,t,o,r)=>{try{let a=await fetch("/onboarding/claim_token",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({invitation_link:t,user_id:o,password:r})});if(!a.ok){let e=await a.text();throw s(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},O=async(e,t,o)=>{try{let r=await fetch("/key/".concat(t,"/regenerate"),{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("Regenerate key Response:",a),a}catch(e){throw console.error("Failed to regenerate key:",e),e}},P=!1,v=null,G=async(e,t,o)=>{try{let t=await fetch("/v2/model/info",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw e+="error shown=".concat(P),P||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),r.ZP.info(e,10),P=!0,v&&clearTimeout(v),v=setTimeout(()=>{P=!1},1e4)),Error("Network response was not ok")}let o=await t.json();return console.log("modelInfoCall:",o),o}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let t=await fetch("/model_group/info",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");let o=await t.json();return console.log("modelHubCall:",o),o}catch(e){throw console.error("Failed to create key:",e),e}},J=async e=>{try{let t=await fetch("/get/allowed_ips",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw Error("Network response was not ok: ".concat(e))}let o=await t.json();return console.log("getAllowedIPs:",o),o.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},I=async(e,t)=>{try{let o=await fetch("/add/allowed_ip",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!o.ok){let e=await o.text();throw Error("Network response was not ok: ".concat(e))}let r=await o.json();return console.log("addAllowedIP:",r),r}catch(e){throw console.error("Failed to add allowed IP:",e),e}},R=async(e,t)=>{try{let o=await fetch("/delete/allowed_ip",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!o.ok){let e=await o.text();throw Error("Network response was not ok: ".concat(e))}let r=await o.json();return console.log("deleteAllowedIP:",r),r}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},V=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,t,o,r)=>{try{let a="/model/streaming_metrics";t&&(a="".concat(a,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(r));let n=await fetch(a,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}return await n.json()}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics/slow_responses";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},z=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics/exceptions";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},Z=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];console.log("in /models calls, globalLitellmHeaderName",c);try{let t="/models";!0===r&&(t+="?return_wildcard_routes=True");let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},M=async e=>{try{let t="/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,t,o,r)=>{try{let a="/global/spend/tags";t&&o&&(a="".concat(a,"?start_date=").concat(t,"&end_date=").concat(o)),r&&(a+="".concat(a,"&tags=").concat(r.join(","))),console.log("in tagsSpendLogsCall:",a);let n=await fetch("".concat(a),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async e=>{try{let t="/global/spend/all_tag_names";console.log("in global/spend/all_tag_names call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},H=async e=>{try{let t="/global/all_end_users";console.log("in global/all_end_users call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},X=async(e,t)=>{try{let o="/user/filter/ui";t.get("user_email")&&(o+="?user_email=".concat(t.get("user_email"))),t.get("user_id")&&(o+="?user_id=".concat(t.get("user_id")));let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,t,o,r,a,n,l,i,d,w)=>{try{let h="/spend/logs/ui",p=new URLSearchParams;t&&p.append("api_key",t),o&&p.append("team_id",o),d&&p.append("min_spend",d.toString()),w&&p.append("max_spend",w.toString()),r&&p.append("request_id",r),a&&p.append("start_date",a),n&&p.append("end_date",n),l&&p.append("page",l.toString()),i&&p.append("page_size",i.toString());let u=p.toString();u&&(h+="?".concat(u));let y=await fetch(h,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!y.ok){let e=await y.text();throw s(e),Error("Network response was not ok")}let f=await y.json();return console.log("Spend Logs Response:",f),f}catch(e){throw console.error("Failed to fetch spend logs:",e),e}},$=async e=>{try{let t=await fetch("/global/spend/logs",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},Y=async e=>{try{let t=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},Q=async(e,t,o,r)=>{try{let a="";a=t?JSON.stringify({api_key:t,startTime:o,endTime:r}):JSON.stringify({startTime:o,endTime:r});let n={method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:a},l=await fetch("/global/spend/end_users",n);if(!l.ok){let e=await l.text();throw s(e),Error("Network response was not ok")}let i=await l.json();return console.log(i),i}catch(e){throw console.error("Failed to create key:",e),e}},W=async(e,t,o,r)=>{try{let a="/global/spend/provider";o&&r&&(a+="?start_date=".concat(o,"&end_date=").concat(r)),t&&(a+="&api_key=".concat(t));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},l=await fetch(a,n);if(!l.ok){let e=await l.text();throw s(e),Error("Network response was not ok")}let i=await l.json();return console.log(i),i}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ee=async(e,t,o)=>{try{let r="/global/activity";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},et=async(e,t,o)=>{try{let r="/global/activity/cache_hits";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},eo=async(e,t,o)=>{try{let r="/global/activity/model";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},er=async(e,t,o,r)=>{try{let a="/global/activity/exceptions";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},s=await fetch(a,n);if(!s.ok)throw await s.text(),Error("Network response was not ok");let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ea=async(e,t,o,r)=>{try{let a="/global/activity/exceptions/deployment";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},s=await fetch(a,n);if(!s.ok)throw await s.text(),Error("Network response was not ok");let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to fetch spend data:",e),e}},en=async e=>{try{let t=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},es=async(e,t)=>{try{let o="/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},ec=async e=>{try{let t=await fetch("/user/available_roles",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");let o=await t.json();return console.log("response from user/available_role",o),o}catch(e){throw e}},el=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let o=await fetch("/team/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ei=async(e,t)=>{try{console.log("Form Values in keyUpdateCall:",t);let o=await fetch("/key/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update key Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ed=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=await fetch("/team/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update Team Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ew=async(e,t)=>{try{console.log("Form Values in modelUpateCall:",t);let o=await fetch("/model/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update model Response:",r),r}catch(e){throw console.error("Failed to update model:",e),e}},eh=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_add",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},ep=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},eu=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,...o.user_email&&{user_email:o.user_email},...o.user_id&&{user_id:o.user_id}})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},ey=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/organization/member_add",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create organization member:",e),e}},ef=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let r={...t};null!==o&&(r.user_role=o),r=JSON.stringify(r);let a=await fetch("/user/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:r});if(!a.ok){let e=await a.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},ek=async(e,t)=>{try{let o="/health/services?service=".concat(t);console.log("Checking Slack Budget Alerts service health");let a=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw s(e),Error(e)}let n=await a.json();return r.ZP.success("Test request to ".concat(t," made - check logs/alerts on ").concat(t," to verify")),n}catch(e){throw console.error("Failed to perform health check:",e),e}},em=async e=>{try{let t=await fetch("/budget/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eg=async(e,t,o)=>{try{let t=await fetch("/get/config/callbacks",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eT=async e=>{try{let t=await fetch("/config/list?config_type=general_settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eE=async e=>{try{let t=await fetch("/config/pass_through_endpoint",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},ej=async(e,t)=>{try{let o=await fetch("/config/field/info?field_name=".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e_=async(e,t)=>{try{let o=await fetch("/config/pass_through_endpoint",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eN=async(e,t,o)=>{try{let a=await fetch("/config/field/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,field_value:o,config_type:"general_settings"})});if(!a.ok){let e=await a.text();throw s(e),Error("Network response was not ok")}let n=await a.json();return r.ZP.success("Successfully updated value!"),n}catch(e){throw console.error("Failed to set callbacks:",e),e}},eC=async(e,t)=>{try{let o=await fetch("/config/field/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,config_type:"general_settings"})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let a=await o.json();return r.ZP.success("Field reset on proxy"),a}catch(e){throw console.error("Failed to get callbacks:",e),e}},eF=async(e,t)=>{try{let o=await fetch("/config/pass_through_endpoint".concat(t),{method:"DELETE",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eb=async(e,t)=>{try{let o=await fetch("/config/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},ex=async e=>{try{let t=await fetch("/health",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to call /health:",e),e}},eS=async e=>{try{let t=await fetch("/sso/get/ui_settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eB=async e=>{try{let t=await fetch("/guardrails/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log("Guardrails list response:",o),o}catch(e){throw console.error("Failed to fetch guardrails list:",e),e}}}}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-57462126852c026f.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-57462126852c026f.js deleted file mode 100644 index 36304defac..0000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/250-57462126852c026f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[250],{19250:function(e,t,o){o.d(t,{$I:function(){return R},AZ:function(){return G},Au:function(){return en},BL:function(){return eg},Br:function(){return _},E9:function(){return ej},EG:function(){return eF},EY:function(){return ex},Eb:function(){return E},FC:function(){return $},Gh:function(){return ed},H1:function(){return x},I1:function(){return T},It:function(){return C},J$:function(){return q},K8:function(){return l},K_:function(){return eb},Lp:function(){return eu},N8:function(){return z},NV:function(){return p},Nc:function(){return ei},O3:function(){return em},OU:function(){return Q},Og:function(){return h},Ov:function(){return g},PT:function(){return J},RQ:function(){return k},Rg:function(){return U},So:function(){return M},Vt:function(){return e_},W_:function(){return S},X:function(){return D},XO:function(){return f},Xd:function(){return es},Xm:function(){return N},YU:function(){return eT},Zr:function(){return u},a6:function(){return F},ao:function(){return eC},b1:function(){return Y},cu:function(){return eh},eH:function(){return I},fP:function(){return L},g:function(){return eS},h3:function(){return K},hT:function(){return el},hy:function(){return w},j2:function(){return H},jA:function(){return eN},jE:function(){return ek},kK:function(){return d},kn:function(){return A},lg:function(){return ec},mR:function(){return Z},m_:function(){return B},mp:function(){return eE},n$:function(){return er},o6:function(){return V},pf:function(){return ef},qI:function(){return y},qm:function(){return i},r6:function(){return b},rs:function(){return j},s0:function(){return O},sN:function(){return ep},t3:function(){return eB},tN:function(){return W},u5:function(){return X},um:function(){return ew},v9:function(){return ea},vh:function(){return ey},wX:function(){return m},wd:function(){return ee},xA:function(){return eo},zg:function(){return et}});var r=o(41021);console.log=function(){};let a=0,n=e=>new Promise(t=>setTimeout(t,e)),s=async e=>{let t=Date.now();t-a>6e4?(e.includes("Authentication Error - Expired Key")&&(r.ZP.info("UI Session Expired. Logging out."),a=t,await n(3e3),document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href="/"),a=t):console.log("Error suppressed to prevent spam:",e)},c="Authorization";function l(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),c=e}let i=async e=>{try{let t=await fetch("/get/litellm_model_cost_map",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}}),o=await t.json();return console.log("received litellm model cost data: ".concat(o)),o}catch(e){throw console.error("Failed to get model cost map:",e),e}},d=async(e,t)=>{try{let o=await fetch("/model/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await o.json();return console.log("API Response:",a),r.ZP.success("Model created successfully. Wait 60s and refresh on 'All Models' page"),a}catch(e){throw console.error("Failed to create key:",e),e}},w=async e=>{try{let t=await fetch("/model/settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},h=async(e,t)=>{console.log("model_id in model delete call: ".concat(t));try{let o=await fetch("/model/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await o.json();return console.log("API Response:",a),r.ZP.success("Model deleted successfully. Restart server to see this."),a}catch(e){throw console.error("Failed to create key:",e),e}},p=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=await fetch("/budget/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},u=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=await fetch("/budget/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=await fetch("/budget/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,t)=>{try{let o=await fetch("/invitation/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=await fetch("/alerting/settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},m=async(e,t,o)=>{try{if(console.log("Form Values in keyCreateCall:",o),o.description&&(o.metadata||(o.metadata={}),o.metadata.description=o.description,delete o.description,o.metadata=JSON.stringify(o.metadata)),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",o);let r=await fetch("/key/generate",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t,o)=>{try{if(console.log("Form Values in keyCreateCall:",o),o.description&&(o.metadata||(o.metadata={}),o.metadata.description=o.description,delete o.description,o.metadata=JSON.stringify(o.metadata)),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",o);let r=await fetch("/user/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t)=>{try{console.log("in keyDeleteCall:",t);let o=await fetch("/key/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{console.log("in userDeleteCall:",t);let o=await fetch("/user/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to delete user(s):",e),e}},j=async(e,t)=>{try{console.log("in teamDeleteCall:",t);let o=await fetch("/team/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to delete key:",e),e}},_=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,n=arguments.length>5?arguments[5]:void 0;try{let l;if(r){l="/user/list";let e=new URLSearchParams;null!=a&&e.append("page",a.toString()),null!=n&&e.append("page_size",n.toString()),l+="?".concat(e.toString())}else l="/user/info","Admin"===o||"Admin Viewer"===o||t&&(l+="?user_id=".concat(t));console.log("Requesting user data from:",l);let i=await fetch(l,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!i.ok){let e=await i.text();throw s(e),Error("Network response was not ok")}let d=await i.json();return console.log("API Response:",d),d}catch(e){throw console.error("Failed to fetch user data:",e),e}},N=async(e,t)=>{try{let o="/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},C=async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{let o="/team/list";console.log("in teamInfoCall"),t&&(o+="?user_id=".concat(t));let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("/team/list API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},F=async e=>{try{console.log("in availableTeamListCall");let t=await fetch("/team/available",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log("/team/available_teams API Response:",o),o}catch(e){throw e}},b=async e=>{try{let t=await fetch("/organization/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to create key:",e),e}},x=async(e,t)=>{try{console.log("Form Values in organizationCreateCall:",t);let o=await fetch("/organization/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},S=async e=>{try{let t="/onboarding/get_token";t+="?invite_link=".concat(e);let o=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},B=async(e,t,o,r)=>{try{let a=await fetch("/onboarding/claim_token",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({invitation_link:t,user_id:o,password:r})});if(!a.ok){let e=await a.text();throw s(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},O=async(e,t,o)=>{try{let r=await fetch("/key/".concat(t,"/regenerate"),{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log("Regenerate key Response:",a),a}catch(e){throw console.error("Failed to regenerate key:",e),e}},P=!1,v=null,G=async(e,t,o)=>{try{let t=await fetch("/v2/model/info",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw e+="error shown=".concat(P),P||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),r.ZP.info(e,10),P=!0,v&&clearTimeout(v),v=setTimeout(()=>{P=!1},1e4)),Error("Network response was not ok")}let o=await t.json();return console.log("modelInfoCall:",o),o}catch(e){throw console.error("Failed to create key:",e),e}},A=async e=>{try{let t=await fetch("/model_group/info",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");let o=await t.json();return console.log("modelHubCall:",o),o}catch(e){throw console.error("Failed to create key:",e),e}},J=async e=>{try{let t=await fetch("/get/allowed_ips",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw Error("Network response was not ok: ".concat(e))}let o=await t.json();return console.log("getAllowedIPs:",o),o.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},I=async(e,t)=>{try{let o=await fetch("/add/allowed_ip",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!o.ok){let e=await o.text();throw Error("Network response was not ok: ".concat(e))}let r=await o.json();return console.log("addAllowedIP:",r),r}catch(e){throw console.error("Failed to add allowed IP:",e),e}},R=async(e,t)=>{try{let o=await fetch("/delete/allowed_ip",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!o.ok){let e=await o.text();throw Error("Network response was not ok: ".concat(e))}let r=await o.json();return console.log("deleteAllowedIP:",r),r}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},V=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,t,o,r)=>{try{let a="/model/streaming_metrics";t&&(a="".concat(a,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(r));let n=await fetch(a,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}return await n.json()}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics/slow_responses";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},z=async(e,t,o,r,a,n,l,i)=>{try{let t="/model/metrics/exceptions";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(n,"&api_key=").concat(l,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},M=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];console.log("in /models calls, globalLitellmHeaderName",c);try{let t="/models";!0===r&&(t+="?return_wildcard_routes=True");let o=await fetch(t,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},Z=async e=>{try{let t="/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},q=async(e,t,o,r)=>{try{let a="/global/spend/tags";t&&o&&(a="".concat(a,"?start_date=").concat(t,"&end_date=").concat(o)),r&&(a+="".concat(a,"&tags=").concat(r.join(","))),console.log("in tagsSpendLogsCall:",a);let n=await fetch("".concat(a),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},D=async e=>{try{let t="/global/spend/all_tag_names";console.log("in global/spend/all_tag_names call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},H=async e=>{try{let t="/global/all_end_users";console.log("in global/all_end_users call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let r=await o.json();return console.log(r),r}catch(e){throw console.error("Failed to create key:",e),e}},X=async(e,t)=>{try{let o="/user/filter/ui";t.get("user_email")&&(o+="?user_email=".concat(t.get("user_email"))),t.get("user_id")&&(o+="?user_id=".concat(t.get("user_id")));let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,t,o,r,a,n,l,i,d,w)=>{try{let h="/spend/logs/ui",p=new URLSearchParams;t&&p.append("api_key",t),o&&p.append("team_id",o),d&&p.append("min_spend",d.toString()),w&&p.append("max_spend",w.toString()),r&&p.append("request_id",r),a&&p.append("start_date",a),n&&p.append("end_date",n),l&&p.append("page",l.toString()),i&&p.append("page_size",i.toString());let u=p.toString();u&&(h+="?".concat(u));let y=await fetch(h,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!y.ok){let e=await y.text();throw s(e),Error("Network response was not ok")}let f=await y.json();return console.log("Spend Logs Response:",f),f}catch(e){throw console.error("Failed to fetch spend logs:",e),e}},$=async e=>{try{let t=await fetch("/global/spend/logs",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},W=async e=>{try{let t=await fetch("/global/spend/keys?limit=5",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},Y=async(e,t,o,r)=>{try{let a="";a=t?JSON.stringify({api_key:t,startTime:o,endTime:r}):JSON.stringify({startTime:o,endTime:r});let n={method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:a},l=await fetch("/global/spend/end_users",n);if(!l.ok){let e=await l.text();throw s(e),Error("Network response was not ok")}let i=await l.json();return console.log(i),i}catch(e){throw console.error("Failed to create key:",e),e}},Q=async(e,t,o,r)=>{try{let a="/global/spend/provider";o&&r&&(a+="?start_date=".concat(o,"&end_date=").concat(r)),t&&(a+="&api_key=".concat(t));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},l=await fetch(a,n);if(!l.ok){let e=await l.text();throw s(e),Error("Network response was not ok")}let i=await l.json();return console.log(i),i}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ee=async(e,t,o)=>{try{let r="/global/activity";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},et=async(e,t,o)=>{try{let r="/global/activity/cache_hits";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},eo=async(e,t,o)=>{try{let r="/global/activity/model";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[c]:"Bearer ".concat(e)}},n=await fetch(r,a);if(!n.ok)throw await n.text(),Error("Network response was not ok");let s=await n.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},er=async(e,t,o,r)=>{try{let a="/global/activity/exceptions";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},s=await fetch(a,n);if(!s.ok)throw await s.text(),Error("Network response was not ok");let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ea=async(e,t,o,r)=>{try{let a="/global/activity/exceptions/deployment";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let n={method:"GET",headers:{[c]:"Bearer ".concat(e)}},s=await fetch(a,n);if(!s.ok)throw await s.text(),Error("Network response was not ok");let l=await s.json();return console.log(l),l}catch(e){throw console.error("Failed to fetch spend data:",e),e}},en=async e=>{try{let t=await fetch("/global/spend/models?limit=5",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log(o),o}catch(e){throw console.error("Failed to create key:",e),e}},es=async(e,t)=>{try{let o="/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let r=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to get requested models:",e),e}},ec=async e=>{try{let t=await fetch("/user/available_roles",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");let o=await t.json();return console.log("response from user/available_role",o),o}catch(e){throw e}},el=async(e,t)=>{try{console.log("Form Values in teamCreateCall:",t);let o=await fetch("/team/new",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ei=async(e,t)=>{try{console.log("Form Values in keyUpdateCall:",t);let o=await fetch("/key/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update key Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ed=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=await fetch("/team/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update Team Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},ew=async(e,t)=>{try{console.log("Form Values in modelUpateCall:",t);let o=await fetch("/model/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let r=await o.json();return console.log("Update model Response:",r),r}catch(e){throw console.error("Failed to update model:",e),e}},eh=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_add",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},ep=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},eu=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/team/member_delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,...o.user_email&&{user_email:o.user_email},...o.user_id&&{user_id:o.user_id}})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},ey=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=await fetch("/organization/member_add",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let a=await r.json();return console.log("API Response:",a),a}catch(e){throw console.error("Failed to create organization member:",e),e}},ef=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let r={...t};null!==o&&(r.user_role=o),r=JSON.stringify(r);let a=await fetch("/user/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:r});if(!a.ok){let e=await a.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},ek=async(e,t)=>{try{let o="/health/services?service=".concat(t);console.log("Checking Slack Budget Alerts service health");let a=await fetch(o,{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw s(e),Error(e)}let n=await a.json();return r.ZP.success("Test request to ".concat(t," made - check logs/alerts on ").concat(t," to verify")),n}catch(e){throw console.error("Failed to perform health check:",e),e}},em=async e=>{try{let t=await fetch("/budget/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eg=async(e,t,o)=>{try{let t=await fetch("/get/config/callbacks",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eT=async e=>{try{let t=await fetch("/config/list?config_type=general_settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eE=async e=>{try{let t=await fetch("/config/pass_through_endpoint",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},ej=async(e,t)=>{try{let o=await fetch("/config/field/info?field_name=".concat(t),{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e_=async(e,t)=>{try{let o=await fetch("/config/pass_through_endpoint",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eN=async(e,t,o)=>{try{let a=await fetch("/config/field/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,field_value:o,config_type:"general_settings"})});if(!a.ok){let e=await a.text();throw s(e),Error("Network response was not ok")}let n=await a.json();return r.ZP.success("Successfully updated value!"),n}catch(e){throw console.error("Failed to set callbacks:",e),e}},eC=async(e,t)=>{try{let o=await fetch("/config/field/delete",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,config_type:"general_settings"})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}let a=await o.json();return r.ZP.success("Field reset on proxy"),a}catch(e){throw console.error("Failed to get callbacks:",e),e}},eF=async(e,t)=>{try{let o=await fetch("/config/pass_through_endpoint".concat(t),{method:"DELETE",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eb=async(e,t)=>{try{let o=await fetch("/config/update",{method:"POST",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!o.ok){let e=await o.text();throw s(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},ex=async e=>{try{let t=await fetch("/health",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}return await t.json()}catch(e){throw console.error("Failed to call /health:",e),e}},eS=async e=>{try{let t=await fetch("/sso/get/ui_settings",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok)throw await t.text(),Error("Network response was not ok");return await t.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eB=async e=>{try{let t=await fetch("/guardrails/list",{method:"GET",headers:{[c]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!t.ok){let e=await t.text();throw s(e),Error("Network response was not ok")}let o=await t.json();return console.log("Guardrails list response:",o),o}catch(e){throw console.error("Failed to fetch guardrails list:",e),e}}}}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/261-45feb99696985c63.js b/litellm/proxy/_experimental/out/_next/static/chunks/261-c5b2a5c7eb59699f.js similarity index 99% rename from ui/litellm-dashboard/out/_next/static/chunks/261-45feb99696985c63.js rename to litellm/proxy/_experimental/out/_next/static/chunks/261-c5b2a5c7eb59699f.js index 251b9de365..62630d6355 100644 --- a/ui/litellm-dashboard/out/_next/static/chunks/261-45feb99696985c63.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/261-c5b2a5c7eb59699f.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[261],{23639:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var a=n(1119),r=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"},o=n(55015),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},77565:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var a=n(1119),r=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"}}]},name:"right",theme:"outlined"},o=n(55015),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},12485:function(e,t,n){"use strict";n.d(t,{Z:function(){return p}});var a=n(5853),r=n(31492),i=n(26898),o=n(65954),s=n(1153),l=n(2265),c=n(35242),u=n(42698);n(64016),n(8710),n(33232);let d=(0,s.fn)("Tab"),p=l.forwardRef((e,t)=>{let{icon:n,className:p,children:g}=e,m=(0,a._T)(e,["icon","className","children"]),b=(0,l.useContext)(c.O),f=(0,l.useContext)(u.Z);return l.createElement(r.O,Object.assign({ref:t,className:(0,o.q)(d("root"),"flex whitespace-nowrap truncate max-w-xs outline-none focus:ring-0 text-tremor-default transition duration-100",f?(0,s.bM)(f,i.K.text).selectTextColor:"solid"===b?"ui-selected:text-tremor-content-emphasis dark:ui-selected:text-dark-tremor-content-emphasis":"ui-selected:text-tremor-brand dark:ui-selected:text-dark-tremor-brand",function(e,t){switch(e){case"line":return(0,o.q)("ui-selected:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","dark:hover:border-dark-tremor-content-emphasis dark:hover:text-dark-tremor-content-emphasis dark:text-dark-tremor-content",t?(0,s.bM)(t,i.K.border).selectBorderColor:"ui-selected:border-tremor-brand dark:ui-selected:border-dark-tremor-brand");case"solid":return(0,o.q)("border-transparent border rounded-tremor-small px-2.5 py-1","ui-selected:border-tremor-border ui-selected:bg-tremor-background ui-selected:shadow-tremor-input hover:text-tremor-content-emphasis ui-selected:text-tremor-brand","dark:ui-selected:border-dark-tremor-border dark:ui-selected:bg-dark-tremor-background dark:ui-selected:shadow-dark-tremor-input dark:hover:text-dark-tremor-content-emphasis dark:ui-selected:text-dark-tremor-brand",t?(0,s.bM)(t,i.K.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,f),p)},m),n?l.createElement(n,{className:(0,o.q)(d("icon"),"flex-none h-5 w-5",g?"mr-2":"")}):null,g?l.createElement("span",null,g):null)});p.displayName="Tab"},18135:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var a=n(5853),r=n(31492),i=n(65954),o=n(1153),s=n(2265);let l=(0,o.fn)("TabGroup"),c=s.forwardRef((e,t)=>{let{defaultIndex:n,index:o,onIndexChange:c,children:u,className:d}=e,p=(0,a._T)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.createElement(r.O.Group,Object.assign({as:"div",ref:t,defaultIndex:n,selectedIndex:o,onChange:c,className:(0,i.q)(l("root"),"w-full",d)},p),u)});c.displayName="TabGroup"},35242:function(e,t,n){"use strict";n.d(t,{O:function(){return c},Z:function(){return d}});var a=n(5853),r=n(2265),i=n(42698);n(64016),n(8710),n(33232);var o=n(31492),s=n(65954);let l=(0,n(1153).fn)("TabList"),c=(0,r.createContext)("line"),u={line:(0,s.q)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,s.q)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},d=r.forwardRef((e,t)=>{let{color:n,variant:d="line",children:p,className:g}=e,m=(0,a._T)(e,["color","variant","children","className"]);return r.createElement(o.O.List,Object.assign({ref:t,className:(0,s.q)(l("root"),"justify-start overflow-x-clip",u[d],g)},m),r.createElement(c.Provider,{value:d},r.createElement(i.Z.Provider,{value:n},p)))});d.displayName="TabList"},29706:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var a=n(5853);n(42698);var r=n(64016);n(8710);var i=n(33232),o=n(65954),s=n(1153),l=n(2265);let c=(0,s.fn)("TabPanel"),u=l.forwardRef((e,t)=>{let{children:n,className:s}=e,u=(0,a._T)(e,["children","className"]),{selectedValue:d}=(0,l.useContext)(i.Z),p=d===(0,l.useContext)(r.Z);return l.createElement("div",Object.assign({ref:t,className:(0,o.q)(c("root"),"w-full mt-2",p?"":"hidden",s),"aria-selected":p?"true":"false"},u),n)});u.displayName="TabPanel"},77991:function(e,t,n){"use strict";n.d(t,{Z:function(){return d}});var a=n(5853),r=n(31492);n(42698);var i=n(64016);n(8710);var o=n(33232),s=n(65954),l=n(1153),c=n(2265);let u=(0,l.fn)("TabPanels"),d=c.forwardRef((e,t)=>{let{children:n,className:l}=e,d=(0,a._T)(e,["children","className"]);return c.createElement(r.O.Panels,Object.assign({as:"div",ref:t,className:(0,s.q)(u("root"),"w-full",l)},d),e=>{let{selectedIndex:t}=e;return c.createElement(o.Z.Provider,{value:{selectedValue:t}},c.Children.map(n,(e,t)=>c.createElement(i.Z.Provider,{value:t},e)))})});d.displayName="TabPanels"},42698:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var a=n(2265),r=n(7084);n(65954);let i=(0,a.createContext)(r.fr.Blue)},64016:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)(0)},8710:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)(void 0)},33232:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)({selectedValue:void 0,handleValueChange:void 0})},93942:function(e,t,n){"use strict";n.d(t,{i:function(){return s}});var a=n(2265),r=n(50506),i=n(13959),o=n(71744);function s(e){return t=>a.createElement(i.ZP,{theme:{token:{motion:!1,zIndexPopupBase:0}}},a.createElement(e,Object.assign({},t)))}t.Z=(e,t,n,i)=>s(s=>{let{prefixCls:l,style:c}=s,u=a.useRef(null),[d,p]=a.useState(0),[g,m]=a.useState(0),[b,f]=(0,r.Z)(!1,{value:s.open}),{getPrefixCls:E}=a.useContext(o.E_),h=E(t||"select",l);a.useEffect(()=>{if(f(!0),"undefined"!=typeof ResizeObserver){let e=new ResizeObserver(e=>{let t=e[0].target;p(t.offsetHeight+8),m(t.offsetWidth)}),t=setInterval(()=>{var a;let r=n?".".concat(n(h)):".".concat(h,"-dropdown"),i=null===(a=u.current)||void 0===a?void 0:a.querySelector(r);i&&(clearInterval(t),e.observe(i))},10);return()=>{clearInterval(t),e.disconnect()}}},[]);let S=Object.assign(Object.assign({},s),{style:Object.assign(Object.assign({},c),{margin:0}),open:b,visible:b,getPopupContainer:()=>u.current});return i&&(S=i(S)),a.createElement("div",{ref:u,style:{paddingBottom:d,position:"relative",minWidth:g}},a.createElement(e,Object.assign({},S)))})},51369:function(e,t,n){"use strict";let a;n.d(t,{Z:function(){return eY}});var r=n(83145),i=n(2265),o=n(18404),s=n(71744),l=n(13959),c=n(8900),u=n(39725),d=n(54537),p=n(55726),g=n(36760),m=n.n(g),b=n(62236),f=n(68710),E=n(55274),h=n(29961),S=n(69819),y=n(73002),T=n(51248),A=e=>{let{type:t,children:n,prefixCls:a,buttonProps:r,close:o,autoFocus:s,emitEvent:l,isSilent:c,quitOnNullishReturnValue:u,actionFn:d}=e,p=i.useRef(!1),g=i.useRef(null),[m,b]=(0,S.Z)(!1),f=function(){null==o||o.apply(void 0,arguments)};i.useEffect(()=>{let e=null;return s&&(e=setTimeout(()=>{var e;null===(e=g.current)||void 0===e||e.focus()})),()=>{e&&clearTimeout(e)}},[]);let E=e=>{e&&e.then&&(b(!0),e.then(function(){b(!1,!0),f.apply(void 0,arguments),p.current=!1},e=>{if(b(!1,!0),p.current=!1,null==c||!c())return Promise.reject(e)}))};return i.createElement(y.ZP,Object.assign({},(0,T.nx)(t),{onClick:e=>{let t;if(!p.current){if(p.current=!0,!d){f();return}if(l){var n;if(t=d(e),u&&!((n=t)&&n.then)){p.current=!1,f(e);return}}else if(d.length)t=d(o),p.current=!1;else if(!(t=d())){f();return}E(t)}},loading:m,prefixCls:a},r,{ref:g}),n)};let R=i.createContext({}),{Provider:I}=R;var N=()=>{let{autoFocusButton:e,cancelButtonProps:t,cancelTextLocale:n,isSilent:a,mergedOkCancel:r,rootPrefixCls:o,close:s,onCancel:l,onConfirm:c}=(0,i.useContext)(R);return r?i.createElement(A,{isSilent:a,actionFn:l,close:function(){null==s||s.apply(void 0,arguments),null==c||c(!1)},autoFocus:"cancel"===e,buttonProps:t,prefixCls:"".concat(o,"-btn")},n):null},_=()=>{let{autoFocusButton:e,close:t,isSilent:n,okButtonProps:a,rootPrefixCls:r,okTextLocale:o,okType:s,onConfirm:l,onOk:c}=(0,i.useContext)(R);return i.createElement(A,{isSilent:n,type:s||"primary",actionFn:c,close:function(){null==t||t.apply(void 0,arguments),null==l||l(!0)},autoFocus:"ok"===e,buttonProps:a,prefixCls:"".concat(r,"-btn")},o)},v=n(49638),w=n(1119),k=n(26365),C=n(71062),O=i.createContext({}),x=n(31686),L=n(2161),D=n(92491),P=n(95814),M=n(18242);function F(e,t,n){var a=t;return!a&&n&&(a="".concat(e,"-").concat(n)),a}function U(e,t){var n=e["page".concat(t?"Y":"X","Offset")],a="scroll".concat(t?"Top":"Left");if("number"!=typeof n){var r=e.document;"number"!=typeof(n=r.documentElement[a])&&(n=r.body[a])}return n}var B=n(47970),G=n(28791),$=i.memo(function(e){return e.children},function(e,t){return!t.shouldUpdate}),H={width:0,height:0,overflow:"hidden",outline:"none"},z=i.forwardRef(function(e,t){var n,a,r,o=e.prefixCls,s=e.className,l=e.style,c=e.title,u=e.ariaId,d=e.footer,p=e.closable,g=e.closeIcon,b=e.onClose,f=e.children,E=e.bodyStyle,h=e.bodyProps,S=e.modalRender,y=e.onMouseDown,T=e.onMouseUp,A=e.holderRef,R=e.visible,I=e.forceRender,N=e.width,_=e.height,v=e.classNames,k=e.styles,C=i.useContext(O).panel,L=(0,G.x1)(A,C),D=(0,i.useRef)(),P=(0,i.useRef)();i.useImperativeHandle(t,function(){return{focus:function(){var e;null===(e=D.current)||void 0===e||e.focus()},changeActive:function(e){var t=document.activeElement;e&&t===P.current?D.current.focus():e||t!==D.current||P.current.focus()}}});var M={};void 0!==N&&(M.width=N),void 0!==_&&(M.height=_),d&&(n=i.createElement("div",{className:m()("".concat(o,"-footer"),null==v?void 0:v.footer),style:(0,x.Z)({},null==k?void 0:k.footer)},d)),c&&(a=i.createElement("div",{className:m()("".concat(o,"-header"),null==v?void 0:v.header),style:(0,x.Z)({},null==k?void 0:k.header)},i.createElement("div",{className:"".concat(o,"-title"),id:u},c))),p&&(r=i.createElement("button",{type:"button",onClick:b,"aria-label":"Close",className:"".concat(o,"-close")},g||i.createElement("span",{className:"".concat(o,"-close-x")})));var F=i.createElement("div",{className:m()("".concat(o,"-content"),null==v?void 0:v.content),style:null==k?void 0:k.content},r,a,i.createElement("div",(0,w.Z)({className:m()("".concat(o,"-body"),null==v?void 0:v.body),style:(0,x.Z)((0,x.Z)({},E),null==k?void 0:k.body)},h),f),n);return i.createElement("div",{key:"dialog-element",role:"dialog","aria-labelledby":c?u:null,"aria-modal":"true",ref:L,style:(0,x.Z)((0,x.Z)({},l),M),className:m()(o,s),onMouseDown:y,onMouseUp:T},i.createElement("div",{tabIndex:0,ref:D,style:H,"aria-hidden":"true"}),i.createElement($,{shouldUpdate:R||I},S?S(F):F),i.createElement("div",{tabIndex:0,ref:P,style:H,"aria-hidden":"true"}))}),j=i.forwardRef(function(e,t){var n=e.prefixCls,a=e.title,r=e.style,o=e.className,s=e.visible,l=e.forceRender,c=e.destroyOnClose,u=e.motionName,d=e.ariaId,p=e.onVisibleChanged,g=e.mousePosition,b=(0,i.useRef)(),f=i.useState(),E=(0,k.Z)(f,2),h=E[0],S=E[1],y={};function T(){var e,t,n,a,r,i=(n={left:(t=(e=b.current).getBoundingClientRect()).left,top:t.top},r=(a=e.ownerDocument).defaultView||a.parentWindow,n.left+=U(r),n.top+=U(r,!0),n);S(g?"".concat(g.x-i.left,"px ").concat(g.y-i.top,"px"):"")}return h&&(y.transformOrigin=h),i.createElement(B.ZP,{visible:s,onVisibleChanged:p,onAppearPrepare:T,onEnterPrepare:T,forceRender:l,motionName:u,removeOnLeave:c,ref:b},function(s,l){var c=s.className,u=s.style;return i.createElement(z,(0,w.Z)({},e,{ref:t,title:a,ariaId:d,prefixCls:n,holderRef:l,style:(0,x.Z)((0,x.Z)((0,x.Z)({},u),r),y),className:m()(o,c)}))})});function V(e){var t=e.prefixCls,n=e.style,a=e.visible,r=e.maskProps,o=e.motionName,s=e.className;return i.createElement(B.ZP,{key:"mask",visible:a,motionName:o,leavedClassName:"".concat(t,"-mask-hidden")},function(e,a){var o=e.className,l=e.style;return i.createElement("div",(0,w.Z)({ref:a,style:(0,x.Z)((0,x.Z)({},l),n),className:m()("".concat(t,"-mask"),o,s)},r))})}function W(e){var t=e.prefixCls,n=void 0===t?"rc-dialog":t,a=e.zIndex,r=e.visible,o=void 0!==r&&r,s=e.keyboard,l=void 0===s||s,c=e.focusTriggerAfterClose,u=void 0===c||c,d=e.wrapStyle,p=e.wrapClassName,g=e.wrapProps,b=e.onClose,f=e.afterOpenChange,E=e.afterClose,h=e.transitionName,S=e.animation,y=e.closable,T=e.mask,A=void 0===T||T,R=e.maskTransitionName,I=e.maskAnimation,N=e.maskClosable,_=e.maskStyle,v=e.maskProps,C=e.rootClassName,O=e.classNames,U=e.styles,B=(0,i.useRef)(),G=(0,i.useRef)(),$=(0,i.useRef)(),H=i.useState(o),z=(0,k.Z)(H,2),W=z[0],q=z[1],Y=(0,D.Z)();function K(e){null==b||b(e)}var Z=(0,i.useRef)(!1),X=(0,i.useRef)(),Q=null;return(void 0===N||N)&&(Q=function(e){Z.current?Z.current=!1:G.current===e.target&&K(e)}),(0,i.useEffect)(function(){o&&(q(!0),(0,L.Z)(G.current,document.activeElement)||(B.current=document.activeElement))},[o]),(0,i.useEffect)(function(){return function(){clearTimeout(X.current)}},[]),i.createElement("div",(0,w.Z)({className:m()("".concat(n,"-root"),C)},(0,M.Z)(e,{data:!0})),i.createElement(V,{prefixCls:n,visible:A&&o,motionName:F(n,R,I),style:(0,x.Z)((0,x.Z)({zIndex:a},_),null==U?void 0:U.mask),maskProps:v,className:null==O?void 0:O.mask}),i.createElement("div",(0,w.Z)({tabIndex:-1,onKeyDown:function(e){if(l&&e.keyCode===P.Z.ESC){e.stopPropagation(),K(e);return}o&&e.keyCode===P.Z.TAB&&$.current.changeActive(!e.shiftKey)},className:m()("".concat(n,"-wrap"),p,null==O?void 0:O.wrapper),ref:G,onClick:Q,style:(0,x.Z)((0,x.Z)((0,x.Z)({zIndex:a},d),null==U?void 0:U.wrapper),{},{display:W?null:"none"})},g),i.createElement(j,(0,w.Z)({},e,{onMouseDown:function(){clearTimeout(X.current),Z.current=!0},onMouseUp:function(){X.current=setTimeout(function(){Z.current=!1})},ref:$,closable:void 0===y||y,ariaId:Y,prefixCls:n,visible:o&&W,onClose:K,onVisibleChanged:function(e){if(e)!function(){if(!(0,L.Z)(G.current,document.activeElement)){var e;null===(e=$.current)||void 0===e||e.focus()}}();else{if(q(!1),A&&B.current&&u){try{B.current.focus({preventScroll:!0})}catch(e){}B.current=null}W&&(null==E||E())}null==f||f(e)},motionName:F(n,h,S)}))))}j.displayName="Content",n(32559);var q=function(e){var t=e.visible,n=e.getContainer,a=e.forceRender,r=e.destroyOnClose,o=void 0!==r&&r,s=e.afterClose,l=e.panelRef,c=i.useState(t),u=(0,k.Z)(c,2),d=u[0],p=u[1],g=i.useMemo(function(){return{panel:l}},[l]);return(i.useEffect(function(){t&&p(!0)},[t]),a||!o||d)?i.createElement(O.Provider,{value:g},i.createElement(C.Z,{open:t||a||d,autoDestroy:!1,getContainer:n,autoLock:t||d},i.createElement(W,(0,w.Z)({},e,{destroyOnClose:o,afterClose:function(){null==s||s(),p(!1)}})))):null};q.displayName="Dialog";var Y=function(e,t,n){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:i.createElement(v.Z,null),r=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if("boolean"==typeof e?!e:void 0===t?!r:!1===t||null===t)return[!1,null];let o="boolean"==typeof t||null==t?a:t;return[!0,n?n(o):o]},K=n(94981),Z=n(95140),X=n(39109),Q=n(65658),J=n(74126);function ee(){}let et=i.createContext({add:ee,remove:ee});var en=n(86586),ea=()=>{let{cancelButtonProps:e,cancelTextLocale:t,onCancel:n}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({onClick:n},e),t)},er=()=>{let{confirmLoading:e,okButtonProps:t,okType:n,okTextLocale:a,onOk:r}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({},(0,T.nx)(n),{loading:e,onClick:r},t),a)},ei=n(92246);function eo(e,t){return i.createElement("span",{className:"".concat(e,"-close-x")},t||i.createElement(v.Z,{className:"".concat(e,"-close-icon")}))}let es=e=>{let t;let{okText:n,okType:a="primary",cancelText:o,confirmLoading:s,onOk:l,onCancel:c,okButtonProps:u,cancelButtonProps:d,footer:p}=e,[g]=(0,E.Z)("Modal",(0,ei.A)()),m={confirmLoading:s,okButtonProps:u,cancelButtonProps:d,okTextLocale:n||(null==g?void 0:g.okText),cancelTextLocale:o||(null==g?void 0:g.cancelText),okType:a,onOk:l,onCancel:c},b=i.useMemo(()=>m,(0,r.Z)(Object.values(m)));return"function"==typeof p||void 0===p?(t=i.createElement(i.Fragment,null,i.createElement(ea,null),i.createElement(er,null)),"function"==typeof p&&(t=p(t,{OkBtn:er,CancelBtn:ea})),t=i.createElement(I,{value:b},t)):t=p,i.createElement(en.n,{disabled:!1},t)};var el=n(12918),ec=n(11699),eu=n(691),ed=n(3104),ep=n(80669),eg=n(352);function em(e){return{position:e,inset:0}}let eb=e=>{let{componentCls:t,antCls:n}=e;return[{["".concat(t,"-root")]:{["".concat(t).concat(n,"-zoom-enter, ").concat(t).concat(n,"-zoom-appear")]:{transform:"none",opacity:0,animationDuration:e.motionDurationSlow,userSelect:"none"},["".concat(t).concat(n,"-zoom-leave ").concat(t,"-content")]:{pointerEvents:"none"},["".concat(t,"-mask")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,height:"100%",backgroundColor:e.colorBgMask,pointerEvents:"none",["".concat(t,"-hidden")]:{display:"none"}}),["".concat(t,"-wrap")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,overflow:"auto",outline:0,WebkitOverflowScrolling:"touch",["&:has(".concat(t).concat(n,"-zoom-enter), &:has(").concat(t).concat(n,"-zoom-appear)")]:{pointerEvents:"none"}})}},{["".concat(t,"-root")]:(0,ec.J$)(e)}]},ef=e=>{let{componentCls:t}=e;return[{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl"},["".concat(t,"-centered")]:{textAlign:"center","&::before":{display:"inline-block",width:0,height:"100%",verticalAlign:"middle",content:'""'},[t]:{top:0,display:"inline-block",paddingBottom:0,textAlign:"start",verticalAlign:"middle"}},["@media (max-width: ".concat(e.screenSMMax,"px)")]:{[t]:{maxWidth:"calc(100vw - 16px)",margin:"".concat((0,eg.bf)(e.marginXS)," auto")},["".concat(t,"-centered")]:{[t]:{flex:1}}}}},{[t]:Object.assign(Object.assign({},(0,el.Wf)(e)),{pointerEvents:"none",position:"relative",top:100,width:"auto",maxWidth:"calc(100vw - ".concat((0,eg.bf)(e.calc(e.margin).mul(2).equal()),")"),margin:"0 auto",paddingBottom:e.paddingLG,["".concat(t,"-title")]:{margin:0,color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.titleFontSize,lineHeight:e.titleLineHeight,wordWrap:"break-word"},["".concat(t,"-content")]:{position:"relative",backgroundColor:e.contentBg,backgroundClip:"padding-box",border:0,borderRadius:e.borderRadiusLG,boxShadow:e.boxShadow,pointerEvents:"auto",padding:e.contentPadding},["".concat(t,"-close")]:Object.assign({position:"absolute",top:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),insetInlineEnd:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),zIndex:e.calc(e.zIndexPopupBase).add(10).equal(),padding:0,color:e.modalCloseIconColor,fontWeight:e.fontWeightStrong,lineHeight:1,textDecoration:"none",background:"transparent",borderRadius:e.borderRadiusSM,width:e.modalCloseBtnSize,height:e.modalCloseBtnSize,border:0,outline:0,cursor:"pointer",transition:"color ".concat(e.motionDurationMid,", background-color ").concat(e.motionDurationMid),"&-x":{display:"flex",fontSize:e.fontSizeLG,fontStyle:"normal",lineHeight:"".concat((0,eg.bf)(e.modalCloseBtnSize)),justifyContent:"center",textTransform:"none",textRendering:"auto"},"&:hover":{color:e.modalIconHoverColor,backgroundColor:e.closeBtnHoverBg,textDecoration:"none"},"&:active":{backgroundColor:e.closeBtnActiveBg}},(0,el.Qy)(e)),["".concat(t,"-header")]:{color:e.colorText,background:e.headerBg,borderRadius:"".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)," 0 0"),marginBottom:e.headerMarginBottom,padding:e.headerPadding,borderBottom:e.headerBorderBottom},["".concat(t,"-body")]:{fontSize:e.fontSize,lineHeight:e.lineHeight,wordWrap:"break-word",padding:e.bodyPadding},["".concat(t,"-footer")]:{textAlign:"end",background:e.footerBg,marginTop:e.footerMarginTop,padding:e.footerPadding,borderTop:e.footerBorderTop,borderRadius:e.footerBorderRadius,["> ".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginInlineStart:e.marginXS}},["".concat(t,"-open")]:{overflow:"hidden"}})},{["".concat(t,"-pure-panel")]:{top:"auto",padding:0,display:"flex",flexDirection:"column",["".concat(t,"-content,\n ").concat(t,"-body,\n ").concat(t,"-confirm-body-wrapper")]:{display:"flex",flexDirection:"column",flex:"auto"},["".concat(t,"-confirm-body")]:{marginBottom:"auto"}}}]},eE=e=>{let{componentCls:t}=e;return{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl",["".concat(t,"-confirm-body")]:{direction:"rtl"}}}}},eh=e=>{let t=e.padding,n=e.fontSizeHeading5,a=e.lineHeightHeading5;return(0,ed.TS)(e,{modalHeaderHeight:e.calc(e.calc(a).mul(n).equal()).add(e.calc(t).mul(2).equal()).equal(),modalFooterBorderColorSplit:e.colorSplit,modalFooterBorderStyle:e.lineType,modalFooterBorderWidth:e.lineWidth,modalIconHoverColor:e.colorIconHover,modalCloseIconColor:e.colorIcon,modalCloseBtnSize:e.fontHeight,modalConfirmIconSize:e.fontHeight,modalTitleHeight:e.calc(e.titleFontSize).mul(e.titleLineHeight).equal()})},eS=e=>({footerBg:"transparent",headerBg:e.colorBgElevated,titleLineHeight:e.lineHeightHeading5,titleFontSize:e.fontSizeHeading5,contentBg:e.colorBgElevated,titleColor:e.colorTextHeading,closeBtnHoverBg:e.wireframe?"transparent":e.colorFillContent,closeBtnActiveBg:e.wireframe?"transparent":e.colorFillContentHover,contentPadding:e.wireframe?0:"".concat((0,eg.bf)(e.paddingMD)," ").concat((0,eg.bf)(e.paddingContentHorizontalLG)),headerPadding:e.wireframe?"".concat((0,eg.bf)(e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,headerBorderBottom:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",headerMarginBottom:e.wireframe?0:e.marginXS,bodyPadding:e.wireframe?e.paddingLG:0,footerPadding:e.wireframe?"".concat((0,eg.bf)(e.paddingXS)," ").concat((0,eg.bf)(e.padding)):0,footerBorderTop:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",footerBorderRadius:e.wireframe?"0 0 ".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)):0,footerMarginTop:e.wireframe?0:e.marginSM,confirmBodyPadding:e.wireframe?"".concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,confirmIconMarginInlineEnd:e.wireframe?e.margin:e.marginSM,confirmBtnsMarginTop:e.wireframe?e.marginLG:e.marginSM});var ey=(0,ep.I$)("Modal",e=>{let t=eh(e);return[ef(t),eE(t),eb(t),(0,eu._y)(t,"zoom")]},eS,{unitless:{titleLineHeight:!0}}),eT=n(64024),eA=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};(0,K.Z)()&&window.document.documentElement&&document.documentElement.addEventListener("click",e=>{a={x:e.pageX,y:e.pageY},setTimeout(()=>{a=null},100)},!0);var eR=e=>{var t;let{getPopupContainer:n,getPrefixCls:r,direction:o,modal:l}=i.useContext(s.E_),c=t=>{let{onCancel:n}=e;null==n||n(t)},{prefixCls:u,className:d,rootClassName:p,open:g,wrapClassName:E,centered:h,getContainer:S,closeIcon:y,closable:T,focusTriggerAfterClose:A=!0,style:R,visible:I,width:N=520,footer:_,classNames:w,styles:k}=e,C=eA(e,["prefixCls","className","rootClassName","open","wrapClassName","centered","getContainer","closeIcon","closable","focusTriggerAfterClose","style","visible","width","footer","classNames","styles"]),O=r("modal",u),x=r(),L=(0,eT.Z)(O),[D,P,M]=ey(O,L),F=m()(E,{["".concat(O,"-centered")]:!!h,["".concat(O,"-wrap-rtl")]:"rtl"===o}),U=null!==_&&i.createElement(es,Object.assign({},e,{onOk:t=>{let{onOk:n}=e;null==n||n(t)},onCancel:c})),[B,G]=Y(T,y,e=>eo(O,e),i.createElement(v.Z,{className:"".concat(O,"-close-icon")}),!0),$=function(e){let t=i.useContext(et),n=i.useRef();return(0,J.zX)(a=>{if(a){let r=e?a.querySelector(e):a;t.add(r),n.current=r}else t.remove(n.current)})}(".".concat(O,"-content")),[H,z]=(0,b.Cn)("Modal",C.zIndex);return D(i.createElement(Q.BR,null,i.createElement(X.Ux,{status:!0,override:!0},i.createElement(Z.Z.Provider,{value:z},i.createElement(q,Object.assign({width:N},C,{zIndex:H,getContainer:void 0===S?n:S,prefixCls:O,rootClassName:m()(P,p,M,L),footer:U,visible:null!=g?g:I,mousePosition:null!==(t=C.mousePosition)&&void 0!==t?t:a,onClose:c,closable:B,closeIcon:G,focusTriggerAfterClose:A,transitionName:(0,f.m)(x,"zoom",e.transitionName),maskTransitionName:(0,f.m)(x,"fade",e.maskTransitionName),className:m()(P,d,null==l?void 0:l.className),style:Object.assign(Object.assign({},null==l?void 0:l.style),R),classNames:Object.assign(Object.assign({wrapper:F},null==l?void 0:l.classNames),w),styles:Object.assign(Object.assign({},null==l?void 0:l.styles),k),panelRef:$}))))))};let eI=e=>{let{componentCls:t,titleFontSize:n,titleLineHeight:a,modalConfirmIconSize:r,fontSize:i,lineHeight:o,modalTitleHeight:s,fontHeight:l,confirmBodyPadding:c}=e,u="".concat(t,"-confirm");return{[u]:{"&-rtl":{direction:"rtl"},["".concat(e.antCls,"-modal-header")]:{display:"none"},["".concat(u,"-body-wrapper")]:Object.assign({},(0,el.dF)()),["&".concat(t," ").concat(t,"-body")]:{padding:c},["".concat(u,"-body")]:{display:"flex",flexWrap:"nowrap",alignItems:"start",["> ".concat(e.iconCls)]:{flex:"none",fontSize:r,marginInlineEnd:e.confirmIconMarginInlineEnd,marginTop:e.calc(e.calc(l).sub(r).equal()).div(2).equal()},["&-has-title > ".concat(e.iconCls)]:{marginTop:e.calc(e.calc(s).sub(r).equal()).div(2).equal()}},["".concat(u,"-paragraph")]:{display:"flex",flexDirection:"column",flex:"auto",rowGap:e.marginXS,maxWidth:"calc(100% - ".concat((0,eg.bf)(e.calc(e.modalConfirmIconSize).add(e.marginSM).equal()),")")},["".concat(u,"-title")]:{color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:n,lineHeight:a},["".concat(u,"-content")]:{color:e.colorText,fontSize:i,lineHeight:o},["".concat(u,"-btns")]:{textAlign:"end",marginTop:e.confirmBtnsMarginTop,["".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginBottom:0,marginInlineStart:e.marginXS}}},["".concat(u,"-error ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorError},["".concat(u,"-warning ").concat(u,"-body > ").concat(e.iconCls,",\n ").concat(u,"-confirm ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorWarning},["".concat(u,"-info ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorInfo},["".concat(u,"-success ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorSuccess}}};var eN=(0,ep.bk)(["Modal","confirm"],e=>[eI(eh(e))],eS,{order:-1e3}),e_=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};function ev(e){let{prefixCls:t,icon:n,okText:a,cancelText:o,confirmPrefixCls:s,type:l,okCancel:g,footer:b,locale:f}=e,h=e_(e,["prefixCls","icon","okText","cancelText","confirmPrefixCls","type","okCancel","footer","locale"]),S=n;if(!n&&null!==n)switch(l){case"info":S=i.createElement(p.Z,null);break;case"success":S=i.createElement(c.Z,null);break;case"error":S=i.createElement(u.Z,null);break;default:S=i.createElement(d.Z,null)}let y=null!=g?g:"confirm"===l,T=null!==e.autoFocusButton&&(e.autoFocusButton||"ok"),[A]=(0,E.Z)("Modal"),R=f||A,v=a||(y?null==R?void 0:R.okText:null==R?void 0:R.justOkText),w=Object.assign({autoFocusButton:T,cancelTextLocale:o||(null==R?void 0:R.cancelText),okTextLocale:v,mergedOkCancel:y},h),k=i.useMemo(()=>w,(0,r.Z)(Object.values(w))),C=i.createElement(i.Fragment,null,i.createElement(N,null),i.createElement(_,null)),O=void 0!==e.title&&null!==e.title,x="".concat(s,"-body");return i.createElement("div",{className:"".concat(s,"-body-wrapper")},i.createElement("div",{className:m()(x,{["".concat(x,"-has-title")]:O})},S,i.createElement("div",{className:"".concat(s,"-paragraph")},O&&i.createElement("span",{className:"".concat(s,"-title")},e.title),i.createElement("div",{className:"".concat(s,"-content")},e.content))),void 0===b||"function"==typeof b?i.createElement(I,{value:k},i.createElement("div",{className:"".concat(s,"-btns")},"function"==typeof b?b(C,{OkBtn:_,CancelBtn:N}):C)):b,i.createElement(eN,{prefixCls:t}))}let ew=e=>{let{close:t,zIndex:n,afterClose:a,open:r,keyboard:o,centered:s,getContainer:l,maskStyle:c,direction:u,prefixCls:d,wrapClassName:p,rootPrefixCls:g,bodyStyle:E,closable:S=!1,closeIcon:y,modalRender:T,focusTriggerAfterClose:A,onConfirm:R,styles:I}=e,N="".concat(d,"-confirm"),_=e.width||416,v=e.style||{},w=void 0===e.mask||e.mask,k=void 0!==e.maskClosable&&e.maskClosable,C=m()(N,"".concat(N,"-").concat(e.type),{["".concat(N,"-rtl")]:"rtl"===u},e.className),[,O]=(0,h.ZP)(),x=i.useMemo(()=>void 0!==n?n:O.zIndexPopupBase+b.u6,[n,O]);return i.createElement(eR,{prefixCls:d,className:C,wrapClassName:m()({["".concat(N,"-centered")]:!!e.centered},p),onCancel:()=>{null==t||t({triggerCancel:!0}),null==R||R(!1)},open:r,title:"",footer:null,transitionName:(0,f.m)(g||"","zoom",e.transitionName),maskTransitionName:(0,f.m)(g||"","fade",e.maskTransitionName),mask:w,maskClosable:k,style:v,styles:Object.assign({body:E,mask:c},I),width:_,zIndex:x,afterClose:a,keyboard:o,centered:s,getContainer:l,closable:S,closeIcon:y,modalRender:T,focusTriggerAfterClose:A},i.createElement(ev,Object.assign({},e,{confirmPrefixCls:N})))};var ek=e=>{let{rootPrefixCls:t,iconPrefixCls:n,direction:a,theme:r}=e;return i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:n,direction:a,theme:r},i.createElement(ew,Object.assign({},e)))},eC=[];let eO="",ex=e=>{var t,n;let{prefixCls:a,getContainer:r,direction:o}=e,l=(0,ei.A)(),c=(0,i.useContext)(s.E_),u=eO||c.getPrefixCls(),d=a||"".concat(u,"-modal"),p=r;return!1===p&&(p=void 0),i.createElement(ek,Object.assign({},e,{rootPrefixCls:u,prefixCls:d,iconPrefixCls:c.iconPrefixCls,theme:c.theme,direction:null!=o?o:c.direction,locale:null!==(n=null===(t=c.locale)||void 0===t?void 0:t.Modal)&&void 0!==n?n:l,getContainer:p}))};function eL(e){let t;let n=(0,l.w6)(),a=document.createDocumentFragment(),s=Object.assign(Object.assign({},e),{close:d,open:!0});function c(){for(var t=arguments.length,n=Array(t),i=0;ie&&e.triggerCancel);e.onCancel&&s&&e.onCancel.apply(e,[()=>{}].concat((0,r.Z)(n.slice(1))));for(let e=0;e{let t=n.getPrefixCls(void 0,eO),r=n.getIconPrefixCls(),s=n.getTheme(),c=i.createElement(ex,Object.assign({},e));(0,o.s)(i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:r,theme:s},n.holderRender?n.holderRender(c):c),a)})}function d(){for(var t=arguments.length,n=Array(t),a=0;a{"function"==typeof e.afterClose&&e.afterClose(),c.apply(this,n)}})).visible&&delete s.visible,u(s)}return u(s),eC.push(d),{destroy:d,update:function(e){u(s="function"==typeof e?e(s):Object.assign(Object.assign({},s),e))}}}function eD(e){return Object.assign(Object.assign({},e),{type:"warning"})}function eP(e){return Object.assign(Object.assign({},e),{type:"info"})}function eM(e){return Object.assign(Object.assign({},e),{type:"success"})}function eF(e){return Object.assign(Object.assign({},e),{type:"error"})}function eU(e){return Object.assign(Object.assign({},e),{type:"confirm"})}var eB=n(93942),eG=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},e$=(0,eB.i)(e=>{let{prefixCls:t,className:n,closeIcon:a,closable:r,type:o,title:l,children:c,footer:u}=e,d=eG(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:p}=i.useContext(s.E_),g=p(),b=t||p("modal"),f=(0,eT.Z)(g),[E,h,S]=ey(b,f),y="".concat(b,"-confirm"),T={};return T=o?{closable:null!=r&&r,title:"",footer:"",children:i.createElement(ev,Object.assign({},e,{prefixCls:b,confirmPrefixCls:y,rootPrefixCls:g,content:c}))}:{closable:null==r||r,title:l,footer:null!==u&&i.createElement(es,Object.assign({},e)),children:c},E(i.createElement(z,Object.assign({prefixCls:b,className:m()(h,"".concat(b,"-pure-panel"),o&&y,o&&"".concat(y,"-").concat(o),n,S,f)},d,{closeIcon:eo(b,a),closable:r},T)))}),eH=n(60804),ez=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},ej=i.forwardRef((e,t)=>{var n,{afterClose:a,config:o}=e,l=ez(e,["afterClose","config"]);let[c,u]=i.useState(!0),[d,p]=i.useState(o),{direction:g,getPrefixCls:m}=i.useContext(s.E_),b=m("modal"),f=m(),h=function(){u(!1);for(var e=arguments.length,t=Array(e),n=0;ne&&e.triggerCancel);d.onCancel&&a&&d.onCancel.apply(d,[()=>{}].concat((0,r.Z)(t.slice(1))))};i.useImperativeHandle(t,()=>({destroy:h,update:e=>{p(t=>Object.assign(Object.assign({},t),e))}}));let S=null!==(n=d.okCancel)&&void 0!==n?n:"confirm"===d.type,[y]=(0,E.Z)("Modal",eH.Z.Modal);return i.createElement(ek,Object.assign({prefixCls:b,rootPrefixCls:f},d,{close:h,open:c,afterClose:()=>{var e;a(),null===(e=d.afterClose)||void 0===e||e.call(d)},okText:d.okText||(S?null==y?void 0:y.okText:null==y?void 0:y.justOkText),direction:d.direction||g,cancelText:d.cancelText||(null==y?void 0:y.cancelText)},l))});let eV=0,eW=i.memo(i.forwardRef((e,t)=>{let[n,a]=function(){let[e,t]=i.useState([]);return[e,i.useCallback(e=>(t(t=>[].concat((0,r.Z)(t),[e])),()=>{t(t=>t.filter(t=>t!==e))}),[])]}();return i.useImperativeHandle(t,()=>({patchElement:a}),[]),i.createElement(i.Fragment,null,n)}));function eq(e){return eL(eD(e))}eR.useModal=function(){let e=i.useRef(null),[t,n]=i.useState([]);i.useEffect(()=>{t.length&&((0,r.Z)(t).forEach(e=>{e()}),n([]))},[t]);let a=i.useCallback(t=>function(a){var o;let s,l;eV+=1;let c=i.createRef(),u=new Promise(e=>{s=e}),d=!1,p=i.createElement(ej,{key:"modal-".concat(eV),config:t(a),ref:c,afterClose:()=>{null==l||l()},isSilent:()=>d,onConfirm:e=>{s(e)}});return(l=null===(o=e.current)||void 0===o?void 0:o.patchElement(p))&&eC.push(l),{destroy:()=>{function e(){var e;null===(e=c.current)||void 0===e||e.destroy()}c.current?e():n(t=>[].concat((0,r.Z)(t),[e]))},update:e=>{function t(){var t;null===(t=c.current)||void 0===t||t.update(e)}c.current?t():n(e=>[].concat((0,r.Z)(e),[t]))},then:e=>(d=!0,u.then(e))}},[]);return[i.useMemo(()=>({info:a(eP),success:a(eM),error:a(eF),warning:a(eD),confirm:a(eU)}),[]),i.createElement(eW,{key:"modal-holder",ref:e})]},eR.info=function(e){return eL(eP(e))},eR.success=function(e){return eL(eM(e))},eR.error=function(e){return eL(eF(e))},eR.warning=eq,eR.warn=eq,eR.confirm=function(e){return eL(eU(e))},eR.destroyAll=function(){for(;eC.length;){let e=eC.pop();e&&e()}},eR.config=function(e){let{rootPrefixCls:t}=e;eO=t},eR._InternalPanelDoNotUseOrYouWillBeFired=e$;var eY=eR},11699:function(e,t,n){"use strict";n.d(t,{J$:function(){return s}});var a=n(352),r=n(37133);let i=new a.E4("antFadeIn",{"0%":{opacity:0},"100%":{opacity:1}}),o=new a.E4("antFadeOut",{"0%":{opacity:1},"100%":{opacity:0}}),s=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],{antCls:n}=e,a="".concat(n,"-fade"),s=t?"&":"";return[(0,r.R)(a,i,o,e.motionDurationMid,t),{["\n ".concat(s).concat(a,"-enter,\n ").concat(s).concat(a,"-appear\n ")]:{opacity:0,animationTimingFunction:"linear"},["".concat(s).concat(a,"-leave")]:{animationTimingFunction:"linear"}}]}},26035:function(e){"use strict";e.exports=function(e,n){for(var a,r,i,o=e||"",s=n||"div",l={},c=0;c4&&m.slice(0,4)===o&&s.test(t)&&("-"===t.charAt(4)?b=o+(n=t.slice(5).replace(l,d)).charAt(0).toUpperCase()+n.slice(1):(g=(p=t).slice(4),t=l.test(g)?p:("-"!==(g=g.replace(c,u)).charAt(0)&&(g="-"+g),o+g)),f=r),new f(b,t))};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,c=/[A-Z]/g;function u(e){return"-"+e.toLowerCase()}function d(e){return e.charAt(1).toUpperCase()}},30466:function(e,t,n){"use strict";var a=n(82855),r=n(64541),i=n(80808),o=n(44987),s=n(72731),l=n(98946);e.exports=a([i,r,o,s,l])},72731:function(e,t,n){"use strict";var a=n(20321),r=n(41757),i=a.booleanish,o=a.number,s=a.spaceSeparated;e.exports=r({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:i,ariaAutoComplete:null,ariaBusy:i,ariaChecked:i,ariaColCount:o,ariaColIndex:o,ariaColSpan:o,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:i,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:i,ariaFlowTo:s,ariaGrabbed:i,ariaHasPopup:null,ariaHidden:i,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:o,ariaLive:null,ariaModal:i,ariaMultiLine:i,ariaMultiSelectable:i,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:o,ariaPressed:i,ariaReadOnly:i,ariaRelevant:null,ariaRequired:i,ariaRoleDescription:s,ariaRowCount:o,ariaRowIndex:o,ariaRowSpan:o,ariaSelected:i,ariaSetSize:o,ariaSort:null,ariaValueMax:o,ariaValueMin:o,ariaValueNow:o,ariaValueText:null,role:null}})},98946:function(e,t,n){"use strict";var a=n(20321),r=n(41757),i=n(53296),o=a.boolean,s=a.overloadedBoolean,l=a.booleanish,c=a.number,u=a.spaceSeparated,d=a.commaSeparated;e.exports=r({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:i,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:d,acceptCharset:u,accessKey:u,action:null,allow:null,allowFullScreen:o,allowPaymentRequest:o,allowUserMedia:o,alt:null,as:null,async:o,autoCapitalize:null,autoComplete:u,autoFocus:o,autoPlay:o,capture:o,charSet:null,checked:o,cite:null,className:u,cols:c,colSpan:null,content:null,contentEditable:l,controls:o,controlsList:u,coords:c|d,crossOrigin:null,data:null,dateTime:null,decoding:null,default:o,defer:o,dir:null,dirName:null,disabled:o,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:o,formTarget:null,headers:u,height:c,hidden:o,high:c,href:null,hrefLang:null,htmlFor:u,httpEquiv:u,id:null,imageSizes:null,imageSrcSet:d,inputMode:null,integrity:null,is:null,isMap:o,itemId:null,itemProp:u,itemRef:u,itemScope:o,itemType:u,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:o,low:c,manifest:null,max:null,maxLength:c,media:null,method:null,min:null,minLength:c,multiple:o,muted:o,name:null,nonce:null,noModule:o,noValidate:o,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:o,optimum:c,pattern:null,ping:u,placeholder:null,playsInline:o,poster:null,preload:null,readOnly:o,referrerPolicy:null,rel:u,required:o,reversed:o,rows:c,rowSpan:c,sandbox:u,scope:null,scoped:o,seamless:o,selected:o,shape:null,size:c,sizes:null,slot:null,span:c,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:d,start:c,step:null,style:null,tabIndex:c,target:null,title:null,translate:null,type:null,typeMustMatch:o,useMap:null,value:l,width:c,wrap:null,align:null,aLink:null,archive:u,axis:null,background:null,bgColor:null,border:c,borderColor:null,bottomMargin:c,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:o,declare:o,event:null,face:null,frame:null,frameBorder:null,hSpace:c,leftMargin:c,link:null,longDesc:null,lowSrc:null,marginHeight:c,marginWidth:c,noResize:o,noHref:o,noShade:o,noWrap:o,object:null,profile:null,prompt:null,rev:null,rightMargin:c,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:c,valueType:null,version:null,vAlign:null,vLink:null,vSpace:c,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:o,disableRemotePlayback:o,prefix:null,property:null,results:c,security:null,unselectable:null}})},53296:function(e,t,n){"use strict";var a=n(38781);e.exports=function(e,t){return a(e,t.toLowerCase())}},38781:function(e){"use strict";e.exports=function(e,t){return t in e?e[t]:t}},41757:function(e,t,n){"use strict";var a=n(96532),r=n(61723),i=n(51351);e.exports=function(e){var t,n,o=e.space,s=e.mustUseProperty||[],l=e.attributes||{},c=e.properties,u=e.transform,d={},p={};for(t in c)n=new i(t,u(l,t),c[t],o),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),d[t]=n,p[a(t)]=t,p[a(n.attribute)]=t;return new r(d,p,o)}},51351:function(e,t,n){"use strict";var a=n(24192),r=n(20321);e.exports=s,s.prototype=new a,s.prototype.defined=!0;var i=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],o=i.length;function s(e,t,n,s){var l,c,u,d=-1;for(s&&(this.space=s),a.call(this,e,t);++d