diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a058144f6..e1488a9083 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -500,6 +500,12 @@ jobs: working_directory: ~/project steps: - checkout + - run: + name: Install PostgreSQL + command: | + sudo apt-get update + sudo apt-get install postgresql postgresql-contrib + echo 'export PATH=/usr/lib/postgresql/*/bin:$PATH' >> $BASH_ENV - setup_google_dns - run: name: Show git commit hash @@ -555,6 +561,7 @@ jobs: pip install "diskcache==5.6.1" pip install "Pillow==10.3.0" pip install "jsonschema==4.22.0" + pip install "pytest-postgresql==7.0.1" - save_cache: paths: - ./venv diff --git a/.github/workflows/test-linting.yml b/.github/workflows/test-linting.yml new file mode 100644 index 0000000000..d117d12de4 --- /dev/null +++ b/.github/workflows/test-linting.yml @@ -0,0 +1,53 @@ +name: LiteLLM Linting + +on: + pull_request: + branches: [ main ] + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + + - name: Install Poetry + uses: snok/install-poetry@v1 + + - name: Install dependencies + run: | + poetry install --with dev + + - name: Run Black formatting check + run: | + cd litellm + poetry run black . --check + cd .. + + - name: Run Ruff linting + run: | + cd litellm + poetry run ruff check . + cd .. + + - name: Run MyPy type checking + run: | + cd litellm + poetry run mypy . --ignore-missing-imports + cd .. + + - name: Check for circular imports + run: | + cd litellm + poetry run python ../tests/documentation_tests/test_circular_imports.py + cd .. + + - name: Check import safety + run: | + poetry run python -c "from litellm import *" || (echo '🚨 import failed, this means you introduced unprotected imports! 🚨'; exit 1) \ No newline at end of file diff --git a/.github/workflows/test-litellm.yml b/.github/workflows/test-litellm.yml new file mode 100644 index 0000000000..12d09725ed --- /dev/null +++ b/.github/workflows/test-litellm.yml @@ -0,0 +1,35 @@ +name: LiteLLM Mock Tests (folder - tests/litellm) + +on: + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - name: Thank You Message + run: | + echo "### 🙏 Thank you for contributing to LiteLLM!" >> $GITHUB_STEP_SUMMARY + echo "Your PR is being tested now. We appreciate your help in making LiteLLM better!" >> $GITHUB_STEP_SUMMARY + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + + - name: Install Poetry + uses: snok/install-poetry@v1 + + - name: Install dependencies + run: | + poetry install --with dev,proxy-dev --extras proxy + poetry run pip install pytest-xdist + + - name: Run tests + run: | + poetry run pytest tests/litellm -x -vv -n 4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1cdedb83fc..e599411328 100644 --- a/.gitignore +++ b/.gitignore @@ -83,4 +83,5 @@ tests/llm_translation/test_vertex_key.json litellm/proxy/migrations/0_init/migration.sql litellm/proxy/db/migrations/0_init/migration.sql litellm/proxy/db/migrations/* -litellm/proxy/migrations/* \ No newline at end of file +litellm/proxy/migrations/*config.yaml +litellm/proxy/migrations/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fb37f32524..dedb37d6dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,44 +6,35 @@ repos: entry: pyright language: system types: [python] - files: ^litellm/ + files: ^(litellm/|litellm_proxy_extras/) - id: isort name: isort entry: isort language: system types: [python] - files: litellm/.*\.py + files: (litellm/|litellm_proxy_extras/).*\.py exclude: ^litellm/__init__.py$ -- repo: https://github.com/psf/black - rev: 24.2.0 - hooks: - - id: black + - id: black + name: black + entry: poetry run black + language: system + types: [python] + files: (litellm/|litellm_proxy_extras/).*\.py - repo: https://github.com/pycqa/flake8 rev: 7.0.0 # The version of flake8 to use hooks: - id: flake8 exclude: ^litellm/tests/|^litellm/proxy/tests/|^litellm/tests/litellm/|^tests/litellm/ additional_dependencies: [flake8-print] - files: litellm/.*\.py - # - id: flake8 - # name: flake8 (router.py function length) - # files: ^litellm/router\.py$ - # args: [--max-function-length=40] - # # additional_dependencies: [flake8-functions] + files: (litellm/|litellm_proxy_extras/).*\.py - repo: https://github.com/python-poetry/poetry rev: 1.8.0 hooks: - id: poetry-check + files: ^(pyproject.toml|litellm-proxy-extras/pyproject.toml)$ - repo: local hooks: - id: check-files-match name: Check if files match entry: python3 ci_cd/check_files_match.py - language: system - # - id: check-file-length - # name: Check file length - # entry: python check_file_length.py - # args: ["10000"] # set your desired maximum number of lines - # language: python - # files: litellm/.*\.py - # exclude: ^litellm/tests/ \ No newline at end of file + language: system \ No newline at end of file diff --git a/Makefile b/Makefile index 6c231d3cc2..a06509312d 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ help: install-dev: poetry install --with dev +install-proxy-dev: + poetry install --with dev,proxy-dev + lint: install-dev poetry run pip install types-requests types-setuptools types-redis types-PyYAML cd litellm && poetry run mypy . --ignore-missing-imports diff --git a/README.md b/README.md index 2d2f71e4d1..1c4e148443 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ PyPI Version - - CircleCI - Y Combinator W23 diff --git a/ci_cd/baseline_db.py b/ci_cd/baseline_db.py new file mode 100644 index 0000000000..ecc080abed --- /dev/null +++ b/ci_cd/baseline_db.py @@ -0,0 +1,60 @@ +import subprocess +from pathlib import Path +from datetime import datetime + + +def create_baseline(): + """Create baseline migration in deploy/migrations""" + try: + # Get paths + root_dir = Path(__file__).parent.parent + deploy_dir = root_dir / "deploy" + migrations_dir = deploy_dir / "migrations" + schema_path = root_dir / "schema.prisma" + + # Create migrations directory + migrations_dir.mkdir(parents=True, exist_ok=True) + + # Create migration_lock.toml if it doesn't exist + lock_file = migrations_dir / "migration_lock.toml" + if not lock_file.exists(): + lock_file.write_text('provider = "postgresql"\n') + + # Create timestamp-based migration directory + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + migration_dir = migrations_dir / f"{timestamp}_baseline" + migration_dir.mkdir(parents=True, exist_ok=True) + + # Generate migration SQL + result = subprocess.run( + [ + "prisma", + "migrate", + "diff", + "--from-empty", + "--to-schema-datamodel", + str(schema_path), + "--script", + ], + capture_output=True, + text=True, + check=True, + ) + + # Write the SQL to migration.sql + migration_file = migration_dir / "migration.sql" + migration_file.write_text(result.stdout) + + print(f"Created baseline migration in {migration_dir}") + return True + + except subprocess.CalledProcessError as e: + print(f"Error running prisma command: {e.stderr}") + return False + except Exception as e: + print(f"Error creating baseline migration: {str(e)}") + return False + + +if __name__ == "__main__": + create_baseline() diff --git a/ci_cd/publish-proxy-extras.sh b/ci_cd/publish-proxy-extras.sh new file mode 100644 index 0000000000..6c83d1f921 --- /dev/null +++ b/ci_cd/publish-proxy-extras.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Exit on error +set -e + +echo "🚀 Building and publishing litellm-proxy-extras" + +# Navigate to litellm-proxy-extras directory +cd "$(dirname "$0")/../litellm-proxy-extras" + +# Build the package +echo "📦 Building package..." +poetry build + +# Publish to PyPI +echo "🌎 Publishing to PyPI..." +poetry publish + +echo "✅ Done! Package published successfully" \ No newline at end of file diff --git a/ci_cd/run_migration.py b/ci_cd/run_migration.py new file mode 100644 index 0000000000..b11a38395c --- /dev/null +++ b/ci_cd/run_migration.py @@ -0,0 +1,95 @@ +import os +import subprocess +from pathlib import Path +from datetime import datetime +import testing.postgresql +import shutil + + +def create_migration(migration_name: str = None): + """ + Create a new migration SQL file in the migrations directory by comparing + current database state with schema + + Args: + migration_name (str): Name for the migration + """ + try: + # Get paths + root_dir = Path(__file__).parent.parent + migrations_dir = root_dir / "litellm-proxy-extras" / "litellm_proxy_extras" / "migrations" + schema_path = root_dir / "schema.prisma" + + # Create temporary PostgreSQL database + with testing.postgresql.Postgresql() as postgresql: + db_url = postgresql.url() + + # Create temporary migrations directory next to schema.prisma + temp_migrations_dir = schema_path.parent / "migrations" + + try: + # Copy existing migrations to temp directory + if temp_migrations_dir.exists(): + shutil.rmtree(temp_migrations_dir) + shutil.copytree(migrations_dir, temp_migrations_dir) + + # Apply existing migrations to temp database + os.environ["DATABASE_URL"] = db_url + subprocess.run( + ["prisma", "migrate", "deploy", "--schema", str(schema_path)], + check=True, + ) + + # Generate diff between current database and schema + result = subprocess.run( + [ + "prisma", + "migrate", + "diff", + "--from-url", + db_url, + "--to-schema-datamodel", + str(schema_path), + "--script", + ], + capture_output=True, + text=True, + check=True, + ) + + if result.stdout.strip(): + # Generate timestamp and create migration directory + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + migration_name = migration_name or "unnamed_migration" + migration_dir = migrations_dir / f"{timestamp}_{migration_name}" + migration_dir.mkdir(parents=True, exist_ok=True) + + # Write the SQL to migration.sql + migration_file = migration_dir / "migration.sql" + migration_file.write_text(result.stdout) + + print(f"Created migration in {migration_dir}") + return True + else: + print("No schema changes detected. Migration not needed.") + return False + + finally: + # Clean up: remove temporary migrations directory + if temp_migrations_dir.exists(): + shutil.rmtree(temp_migrations_dir) + + except subprocess.CalledProcessError as e: + print(f"Error generating migration: {e.stderr}") + return False + except Exception as e: + print(f"Error creating migration: {str(e)}") + return False + + +if __name__ == "__main__": + # If running directly, can optionally pass migration name as argument + import sys + + migration_name = sys.argv[1] if len(sys.argv) > 1 else None + create_migration(migration_name) diff --git a/deploy/charts/litellm-helm/templates/migrations-job.yaml b/deploy/charts/litellm-helm/templates/migrations-job.yaml index e994c45548..1c4b6817fa 100644 --- a/deploy/charts/litellm-helm/templates/migrations-job.yaml +++ b/deploy/charts/litellm-helm/templates/migrations-job.yaml @@ -65,6 +65,6 @@ spec: tolerations: {{- toYaml . | nindent 8 }} {{- end }} - ttlSecondsAfterFinished: {{ .Values.migrationJob.ttlSecondsAfterFinished }} + ttlSecondsAfterFinished: {{ .Values.migrationJob.ttlSecondsAfterFinished }} backoffLimit: {{ .Values.migrationJob.backoffLimit }} {{- end }} diff --git a/docs/my-website/docs/mcp.md b/docs/my-website/docs/mcp.md index 744c23644e..0947c494c7 100644 --- a/docs/my-website/docs/mcp.md +++ b/docs/my-website/docs/mcp.md @@ -4,21 +4,177 @@ import Image from '@theme/IdealImage'; # /mcp [BETA] - Model Context Protocol -Use Model Context Protocol with LiteLLM +## Expose MCP tools on LiteLLM Proxy Server +This allows you to define tools that can be called by any MCP compatible client. Define your `mcp_servers` with LiteLLM and all your clients can list and call available tools.

LiteLLM MCP Architecture: Use MCP tools with all LiteLLM supported models

+#### How it works -## Overview +LiteLLM exposes the following MCP endpoints: -LiteLLM acts as a MCP bridge to utilize MCP tools with all LiteLLM supported models. LiteLLM offers the following features for using MCP +- `/mcp/tools/list` - List all available tools +- `/mcp/tools/call` - Call a specific tool with the provided arguments + +When MCP clients connect to LiteLLM they can follow this workflow: + +1. Connect to the LiteLLM MCP server +2. List all available tools on LiteLLM +3. Client makes LLM API request with tool call(s) +4. LLM API returns which tools to call and with what arguments +5. MCP client makes MCP tool calls to LiteLLM +6. LiteLLM makes the tool calls to the appropriate MCP server +7. LiteLLM returns the tool call results to the MCP client + +#### Usage + +#### 1. Define your tools on under `mcp_servers` in your config.yaml file. + +LiteLLM allows you to define your tools on the `mcp_servers` section in your config.yaml file. All tools listed here will be available to MCP clients (when they connect to LiteLLM and call `list_tools`). + +```yaml title="config.yaml" showLineNumbers +model_list: + - model_name: gpt-4o + litellm_params: + model: openai/gpt-4o + api_key: sk-xxxxxxx + +mcp_servers: + { + "zapier_mcp": { + "url": "https://actions.zapier.com/mcp/sk-akxxxxx/sse" + }, + "fetch": { + "url": "http://localhost:8000/sse" + } + } +``` + + +#### 2. Start LiteLLM Gateway + + + + +```shell title="Docker Run" showLineNumbers +docker run -d \ + -p 4000:4000 \ + -e OPENAI_API_KEY=$OPENAI_API_KEY \ + --name my-app \ + -v $(pwd)/my_config.yaml:/app/config.yaml \ + my-app:latest \ + --config /app/config.yaml \ + --port 4000 \ + --detailed_debug \ +``` + + + + + +```shell title="litellm pip" showLineNumbers +litellm --config config.yaml --detailed_debug +``` + + + + + +#### 3. Make an LLM API request + +In this example we will do the following: + +1. Use MCP client to list MCP tools on LiteLLM Proxy +2. Use `transform_mcp_tool_to_openai_tool` to convert MCP tools to OpenAI tools +3. Provide the MCP tools to `gpt-4o` +4. Handle tool call from `gpt-4o` +5. Convert OpenAI tool call to MCP tool call +6. Execute tool call on MCP server + +```python title="MCP Client List Tools" showLineNumbers +import asyncio +from openai import AsyncOpenAI +from openai.types.chat import ChatCompletionUserMessageParam +from mcp import ClientSession +from mcp.client.sse import sse_client +from litellm.experimental_mcp_client.tools import ( + transform_mcp_tool_to_openai_tool, + transform_openai_tool_call_request_to_mcp_tool_call_request, +) + + +async def main(): + # Initialize clients + + # point OpenAI client to LiteLLM Proxy + client = AsyncOpenAI(api_key="sk-1234", base_url="http://localhost:4000") + + # Point MCP client to LiteLLM Proxy + async with sse_client("http://localhost:4000/mcp/") as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + # 1. List MCP tools on LiteLLM Proxy + mcp_tools = await session.list_tools() + print("List of MCP tools for MCP server:", mcp_tools.tools) + + # Create message + messages = [ + ChatCompletionUserMessageParam( + content="Send an email about LiteLLM supporting MCP", role="user" + ) + ] + + # 2. Use `transform_mcp_tool_to_openai_tool` to convert MCP tools to OpenAI tools + # Since OpenAI only supports tools in the OpenAI format, we need to convert the MCP tools to the OpenAI format. + openai_tools = [ + transform_mcp_tool_to_openai_tool(tool) for tool in mcp_tools.tools + ] + + # 3. Provide the MCP tools to `gpt-4o` + response = await client.chat.completions.create( + model="gpt-4o", + messages=messages, + tools=openai_tools, + tool_choice="auto", + ) + + # 4. Handle tool call from `gpt-4o` + if response.choices[0].message.tool_calls: + tool_call = response.choices[0].message.tool_calls[0] + if tool_call: + + # 5. Convert OpenAI tool call to MCP tool call + # Since MCP servers expect tools in the MCP format, we need to convert the OpenAI tool call to the MCP format. + # This is done using litellm.experimental_mcp_client.tools.transform_openai_tool_call_request_to_mcp_tool_call_request + mcp_call = ( + transform_openai_tool_call_request_to_mcp_tool_call_request( + openai_tool=tool_call.model_dump() + ) + ) + + # 6. Execute tool call on MCP server + result = await session.call_tool( + name=mcp_call.name, arguments=mcp_call.arguments + ) + + print("Result:", result) + + +# Run it +asyncio.run(main()) +``` + +## LiteLLM Python SDK MCP Bridge + +LiteLLM Python SDK acts as a MCP bridge to utilize MCP tools with all LiteLLM supported models. LiteLLM offers the following features for using MCP - **List** Available MCP Tools: OpenAI clients can view all available MCP tools - `litellm.experimental_mcp_client.load_mcp_tools` to list all available MCP tools @@ -26,8 +182,6 @@ LiteLLM acts as a MCP bridge to utilize MCP tools with all LiteLLM supported mod - `litellm.experimental_mcp_client.call_openai_tool` to call an OpenAI tool on an MCP server -## Usage - ### 1. List Available MCP Tools In this example we'll use `litellm.experimental_mcp_client.load_mcp_tools` to list all available MCP tools on any MCP server. This method can be used in two ways: @@ -270,209 +424,4 @@ async with stdio_client(server_params) as (read, write): ``` - - -## Advanced Usage - -### Expose MCP tools on LiteLLM Proxy Server - -This allows you to define tools that can be called by any MCP compatible client. Define your mcp_tools with LiteLLM and all your clients can list and call available tools. - -#### How it works - -LiteLLM exposes the following MCP endpoints: - -- `/mcp/list_tools` - List all available tools -- `/mcp/call_tool` - Call a specific tool with the provided arguments - -When MCP clients connect to LiteLLM they can follow this workflow: - -1. Connect to the LiteLLM MCP server -2. List all available tools on LiteLLM -3. Client makes LLM API request with tool call(s) -4. LLM API returns which tools to call and with what arguments -5. MCP client makes tool calls to LiteLLM -6. LiteLLM makes the tool calls to the appropriate handlers -7. LiteLLM returns the tool call results to the MCP client - -#### Usage - -#### 1. Define your tools on mcp_tools - -LiteLLM allows you to define your tools on the `mcp_tools` section in your config.yaml file. All tools listed here will be available to MCP clients (when they connect to LiteLLM and call `list_tools`). - -```yaml -model_list: - - model_name: gpt-4o - litellm_params: - model: openai/gpt-4o - api_key: sk-xxxxxxx - - - -mcp_tools: - - name: "get_current_time" - description: "Get the current time" - input_schema: { - "type": "object", - "properties": { - "format": { - "type": "string", - "description": "The format of the time to return", - "enum": ["short"] - } - } - } - handler: "mcp_tools.get_current_time" -``` - -#### 2. Define a handler for your tool - -Create a new file called `mcp_tools.py` and add this code. The key method here is `get_current_time` which gets executed when the `get_current_time` tool is called. - -```python -# mcp_tools.py - -from datetime import datetime - -def get_current_time(format: str = "short"): - """ - Simple handler for the 'get_current_time' tool. - - Args: - format (str): The format of the time to return ('short'). - - Returns: - str: The current time formatted as 'HH:MM'. - """ - # Get the current time - current_time = datetime.now() - - # Format the time as 'HH:MM' - return current_time.strftime('%H:%M') -``` - -#### 3. Start LiteLLM Gateway - - - - -Mount your `mcp_tools.py` on the LiteLLM Docker container. - -```shell -docker run -d \ - -p 4000:4000 \ - -e OPENAI_API_KEY=$OPENAI_API_KEY \ - --name my-app \ - -v $(pwd)/my_config.yaml:/app/config.yaml \ - -v $(pwd)/mcp_tools.py:/app/mcp_tools.py \ - my-app:latest \ - --config /app/config.yaml \ - --port 4000 \ - --detailed_debug \ -``` - - - - - -```shell -litellm --config config.yaml --detailed_debug -``` - - - - - -#### 4. Make an LLM API request - - - -```python -import asyncio -from langchain_mcp_adapters.tools import load_mcp_tools -from langchain_openai import ChatOpenAI -from langgraph.prebuilt import create_react_agent -from mcp import ClientSession -from mcp.client.sse import sse_client - - -async def main(): - # Initialize the model with your API key - model = ChatOpenAI(model="gpt-4o") - - # Connect to the MCP server - async with sse_client(url="http://localhost:4000/mcp/") as (read, write): - async with ClientSession(read, write) as session: - # Initialize the session - print("Initializing session...") - await session.initialize() - print("Session initialized") - - # Load available tools from MCP - print("Loading tools...") - tools = await load_mcp_tools(session) - print(f"Loaded {len(tools)} tools") - - # Create a ReAct agent with the model and tools - agent = create_react_agent(model, tools) - - # Run the agent with a user query - user_query = "What's the weather in Tokyo?" - print(f"Asking: {user_query}") - agent_response = await agent.ainvoke({"messages": user_query}) - print("Agent response:") - print(agent_response) - - -if __name__ == "__main__": - asyncio.run(main()) - -``` - - -### Specification for `mcp_tools` - -The `mcp_tools` section in your LiteLLM config defines tools that can be called by MCP-compatible clients. - -#### Tool Definition Format - -```yaml -mcp_tools: - - name: string # Required: Name of the tool - description: string # Required: Description of what the tool does - input_schema: object # Required: JSON Schema defining the tool's input parameters - handler: string # Required: Path to the function that implements the tool -``` - -#### Field Details - -- `name`: A unique identifier for the tool -- `description`: A clear description of what the tool does, used by LLMs to determine when to call it -- `input_schema`: JSON Schema object defining the expected input parameters -- `handler`: String path to the Python function that implements the tool (e.g., "module.submodule.function_name") - -#### Example Tool Definition - -```yaml -mcp_tools: - - name: "get_current_time" - description: "Get the current time in a specified format" - input_schema: { - "type": "object", - "properties": { - "format": { - "type": "string", - "description": "The format of the time to return", - "enum": ["short", "long", "iso"] - }, - "timezone": { - "type": "string", - "description": "The timezone to use (e.g., 'UTC', 'America/New_York')", - "default": "UTC" - } - }, - "required": ["format"] - } - handler: "mcp_tools.get_current_time" -``` + \ No newline at end of file diff --git a/docs/my-website/docs/providers/bedrock.md b/docs/my-website/docs/providers/bedrock.md index 4f88fdb39b..95e0c90bd7 100644 --- a/docs/my-website/docs/providers/bedrock.md +++ b/docs/my-website/docs/providers/bedrock.md @@ -664,6 +664,58 @@ curl http://0.0.0.0:4000/v1/chat/completions \ +## Usage - Latency Optimized Inference + +Valid from v1.65.1+ + + + + +```python +from litellm import completion + +response = completion( + model="bedrock/anthropic.claude-3-7-sonnet-20250219-v1:0", + messages=[{"role": "user", "content": "What is the capital of France?"}], + performanceConfig={"latency": "optimized"}, +) +``` + + + + +1. Setup config.yaml + +```yaml +model_list: + - model_name: bedrock-claude-3-7 + litellm_params: + model: bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0 + performanceConfig: {"latency": "optimized"} # 👈 EITHER HERE OR ON REQUEST +``` + +2. Start proxy + +```bash +litellm --config /path/to/config.yaml +``` + +3. Test it! + +```bash +curl http://0.0.0.0:4000/v1/chat/completions \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $LITELLM_KEY" \ + -d '{ + "model": "bedrock-claude-3-7", + "messages": [{"role": "user", "content": "What is the capital of France?"}], + "performanceConfig": {"latency": "optimized"} # 👈 EITHER HERE OR ON CONFIG.YAML + }' +``` + + + + ## Usage - Bedrock Guardrails Example of using [Bedrock Guardrails with LiteLLM](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails-use-converse-api.html) @@ -1776,6 +1828,7 @@ response = completion( ) ``` + 1. Setup config.yaml @@ -1820,11 +1873,13 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \ ``` + ### SSO Login (AWS Profile) - Set `AWS_PROFILE` environment variable - Make bedrock completion call + ```python import os from litellm import completion @@ -1940,9 +1995,6 @@ curl -L -X POST 'http://0.0.0.0:4000/v1/images/generations' \ "colorGuidedGenerationParams":{"colors":["#FFFFFF"]} }' ``` - - - | Model Name | Function Call | |-------------------------|---------------------------------------------| diff --git a/docs/my-website/docs/providers/gemini.md b/docs/my-website/docs/providers/gemini.md index 4a6cfdf1a3..8171a9dd04 100644 --- a/docs/my-website/docs/providers/gemini.md +++ b/docs/my-website/docs/providers/gemini.md @@ -589,8 +589,10 @@ response = litellm.completion( "content": [ {"type": "text", "text": "Please summarize the audio."}, { - "type": "image_url", - "image_url": "data:audio/mp3;base64,{}".format(encoded_data), # 👈 SET MIME_TYPE + DATA + "type": "file", + "file": { + "file_data": "data:audio/mp3;base64,{}".format(encoded_data), # 👈 SET MIME_TYPE + DATA + } }, ], } @@ -640,8 +642,11 @@ response = litellm.completion( "content": [ {"type": "text", "text": "Please summarize the file."}, { - "type": "image_url", - "image_url": "https://storage..." # 👈 SET THE IMG URL + "type": "file", + "file": { + "file_id": "https://storage...", # 👈 SET THE IMG URL + "format": "application/pdf" # OPTIONAL + } }, ], } @@ -668,8 +673,11 @@ response = litellm.completion( "content": [ {"type": "text", "text": "Please summarize the file."}, { - "type": "image_url", - "image_url": "gs://..." # 👈 SET THE cloud storage bucket url + "type": "file", + "file": { + "file_id": "gs://storage...", # 👈 SET THE IMG URL + "format": "application/pdf" # OPTIONAL + } }, ], } diff --git a/docs/my-website/docs/providers/openai.md b/docs/my-website/docs/providers/openai.md index 794f3da647..9ab9061aaa 100644 --- a/docs/my-website/docs/providers/openai.md +++ b/docs/my-website/docs/providers/openai.md @@ -325,6 +325,74 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \ | fine tuned `gpt-3.5-turbo-0613` | `response = completion(model="ft:gpt-3.5-turbo-0613", messages=messages)` | +## OpenAI Audio Transcription + +LiteLLM supports OpenAI Audio Transcription endpoint. + +Supported models: + +| Model Name | Function Call | +|---------------------------|-----------------------------------------------------------------| +| `whisper-1` | `response = completion(model="whisper-1", file=audio_file)` | +| `gpt-4o-transcribe` | `response = completion(model="gpt-4o-transcribe", file=audio_file)` | +| `gpt-4o-mini-transcribe` | `response = completion(model="gpt-4o-mini-transcribe", file=audio_file)` | + + + + +```python +from litellm import transcription +import os + +# set api keys +os.environ["OPENAI_API_KEY"] = "" +audio_file = open("/path/to/audio.mp3", "rb") + +response = transcription(model="gpt-4o-transcribe", file=audio_file) + +print(f"response: {response}") +``` + + + + +1. Setup config.yaml + +```yaml +model_list: +- model_name: gpt-4o-transcribe + litellm_params: + model: gpt-4o-transcribe + api_key: os.environ/OPENAI_API_KEY + model_info: + mode: audio_transcription + +general_settings: + master_key: sk-1234 +``` + +2. Start the proxy + +```bash +litellm --config config.yaml +``` + +3. Test it! + +```bash +curl --location 'http://0.0.0.0:8000/v1/audio/transcriptions' \ +--header 'Authorization: Bearer sk-1234' \ +--form 'file=@"/Users/krrishdholakia/Downloads/gettysburg.wav"' \ +--form 'model="gpt-4o-transcribe"' +``` + + + + + + + + ## Advanced ### Getting OpenAI API Response Headers diff --git a/docs/my-website/docs/providers/vertex.md b/docs/my-website/docs/providers/vertex.md index 10ac13ecaf..ab13a51137 100644 --- a/docs/my-website/docs/providers/vertex.md +++ b/docs/my-website/docs/providers/vertex.md @@ -1369,6 +1369,103 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \ +## Gemini Pro +| Model Name | Function Call | +|------------------|--------------------------------------| +| gemini-pro | `completion('gemini-pro', messages)`, `completion('vertex_ai/gemini-pro', messages)` | + +## Fine-tuned Models + +You can call fine-tuned Vertex AI Gemini models through LiteLLM + +| Property | Details | +|----------|---------| +| Provider Route | `vertex_ai/gemini/{MODEL_ID}` | +| Vertex Documentation | [Vertex AI - Fine-tuned Gemini Models](https://cloud.google.com/vertex-ai/generative-ai/docs/models/gemini-use-supervised-tuning#test_the_tuned_model_with_a_prompt)| +| Supported Operations | `/chat/completions`, `/completions`, `/embeddings`, `/images` | + +To use a model that follows the `/gemini` request/response format, simply set the model parameter as + +```python title="Model parameter for calling fine-tuned gemini models" +model="vertex_ai/gemini/" +``` + + + + +```python showLineNumbers title="Example" +import litellm +import os + +## set ENV variables +os.environ["VERTEXAI_PROJECT"] = "hardy-device-38811" +os.environ["VERTEXAI_LOCATION"] = "us-central1" + +response = litellm.completion( + model="vertex_ai/gemini/", # e.g. vertex_ai/gemini/4965075652664360960 + messages=[{ "content": "Hello, how are you?","role": "user"}], +) +``` + + + + +1. Add Vertex Credentials to your env + +```bash title="Authenticate to Vertex AI" +!gcloud auth application-default login +``` + +2. Setup config.yaml + +```yaml showLineNumbers title="Add to litellm config" +- model_name: finetuned-gemini + litellm_params: + model: vertex_ai/gemini/ + vertex_project: + vertex_location: +``` + +3. Test it! + + + + +```python showLineNumbers title="Example request" +from openai import OpenAI + +client = OpenAI( + api_key="your-litellm-key", + base_url="http://0.0.0.0:4000" +) + +response = client.chat.completions.create( + model="finetuned-gemini", + messages=[ + {"role": "user", "content": "hi"} + ] +) +print(response) +``` + + + + +```bash showLineNumbers title="Example request" +curl --location 'https://0.0.0.0:4000/v1/chat/completions' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: ' \ +--data '{"model": "finetuned-gemini" ,"messages":[{"role": "user", "content":[{"type": "text", "text": "hi"}]}]}' +``` + + + + + + + + + ## Model Garden :::tip @@ -1479,67 +1576,6 @@ response = completion( -## Gemini Pro -| Model Name | Function Call | -|------------------|--------------------------------------| -| gemini-pro | `completion('gemini-pro', messages)`, `completion('vertex_ai/gemini-pro', messages)` | - -## Fine-tuned Models - -Fine tuned models on vertex have a numerical model/endpoint id. - - - - -```python -from litellm import completion -import os - -## set ENV variables -os.environ["VERTEXAI_PROJECT"] = "hardy-device-38811" -os.environ["VERTEXAI_LOCATION"] = "us-central1" - -response = completion( - model="vertex_ai/", # e.g. vertex_ai/4965075652664360960 - messages=[{ "content": "Hello, how are you?","role": "user"}], - base_model="vertex_ai/gemini-1.5-pro" # the base model - used for routing -) -``` - - - - -1. Add Vertex Credentials to your env - -```bash -!gcloud auth application-default login -``` - -2. Setup config.yaml - -```yaml -- model_name: finetuned-gemini - litellm_params: - model: vertex_ai/ - vertex_project: - vertex_location: - model_info: - base_model: vertex_ai/gemini-1.5-pro # IMPORTANT -``` - -3. Test it! - -```bash -curl --location 'https://0.0.0.0:4000/v1/chat/completions' \ ---header 'Content-Type: application/json' \ ---header 'Authorization: ' \ ---data '{"model": "finetuned-gemini" ,"messages":[{"role": "user", "content":[{"type": "text", "text": "hi"}]}]}' -``` - - - - - ## Gemini Pro Vision | Model Name | Function Call | @@ -1684,23 +1720,25 @@ assert isinstance( ``` -## Usage - PDF / Videos / etc. Files +## Usage - PDF / Videos / Audio etc. Files Pass any file supported by Vertex AI, through LiteLLM. -LiteLLM Supports the following image types passed in url +LiteLLM Supports the following file types passed in url. + +Using `file` message type for VertexAI is live from v1.65.1+ ``` -Images with Cloud Storage URIs - gs://cloud-samples-data/generative-ai/image/boats.jpeg -Images with direct links - https://storage.googleapis.com/github-repo/img/gemini/intro/landmark3.jpg +Files with Cloud Storage URIs - gs://cloud-samples-data/generative-ai/image/boats.jpeg +Files with direct links - https://storage.googleapis.com/github-repo/img/gemini/intro/landmark3.jpg Videos with Cloud Storage URIs - https://storage.googleapis.com/github-repo/img/gemini/multimodality_usecases_overview/pixel8.mp4 -Base64 Encoded Local Images +Base64 Encoded Local Files ``` -### **Using `gs://`** +### **Using `gs://` or any URL** ```python from litellm import completion @@ -1712,8 +1750,11 @@ response = completion( "content": [ {"type": "text", "text": "You are a very professional document summarization specialist. Please summarize the given document."}, { - "type": "image_url", - "image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf", # 👈 PDF + "type": "file", + "file": { + "file_id": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf", + "format": "application/pdf" # OPTIONAL - specify mime-type + } }, ], } @@ -1747,8 +1788,16 @@ response = completion( "content": [ {"type": "text", "text": "You are a very professional document summarization specialist. Please summarize the given document."}, { - "type": "image_url", - "image_url": f"data:application/pdf;base64,{encoded_file}", # 👈 PDF + "type": "file", + "file": { + "file_data": f"data:application/pdf;base64,{encoded_file}", # 👈 PDF + } + }, + { + "type": "audio_input", + "audio_input { + "audio_input": f"data:audio/mp3;base64,{encoded_file}", # 👈 AUDIO File ('file' message works as too) + } }, ], } @@ -1794,8 +1843,11 @@ curl http://0.0.0.0:4000/v1/chat/completions \ "text": "You are a very professional document summarization specialist. Please summarize the given document" }, { - "type": "image_url", - "image_url": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf" # 👈 PDF + "type": "file", + "file": { + "file_id": "gs://cloud-samples-data/generative-ai/pdf/2403.05530.pdf", + "format": "application/pdf" # OPTIONAL + } } } ] @@ -1822,11 +1874,18 @@ curl http://0.0.0.0:4000/v1/chat/completions \ "text": "You are a very professional document summarization specialist. Please summarize the given document" }, { - "type": "image_url", - "image_url": "data:application/pdf;base64,{encoded_file}" # 👈 PDF - } - } - ] + "type": "file", + "file": { + "file_data": f"data:application/pdf;base64,{encoded_file}", # 👈 PDF + }, + }, + { + "type": "audio_input", + "audio_input { + "audio_input": f"data:audio/mp3;base64,{encoded_file}", # 👈 AUDIO File ('file' message works as too) + } + }, + ] } ], "max_tokens": 300 @@ -1836,6 +1895,7 @@ curl http://0.0.0.0:4000/v1/chat/completions \ + ## Chat Models | Model Name | Function Call | |------------------|--------------------------------------| @@ -2044,7 +2104,12 @@ print(response) ## **Multi-Modal Embeddings** -Usage + +Known Limitations: +- Only supports 1 image / video / image per request +- Only supports GCS or base64 encoded images / videos + +### Usage @@ -2260,6 +2325,115 @@ print(f"Text Embedding: {embeddings.text_embedding}") +### Text + Image + Video Embeddings + + + + +Text + Image + +```python +response = await litellm.aembedding( + model="vertex_ai/multimodalembedding@001", + input=["hey", "gs://cloud-samples-data/vertex-ai/llm/prompts/landmark1.png"] # will be sent as a gcs image +) +``` + +Text + Video + +```python +response = await litellm.aembedding( + model="vertex_ai/multimodalembedding@001", + input=["hey", "gs://my-bucket/embeddings/supermarket-video.mp4"] # will be sent as a gcs image +) +``` + +Image + Video + +```python +response = await litellm.aembedding( + model="vertex_ai/multimodalembedding@001", + input=["gs://cloud-samples-data/vertex-ai/llm/prompts/landmark1.png", "gs://my-bucket/embeddings/supermarket-video.mp4"] # will be sent as a gcs image +) +``` + + + + + +1. Add model to config.yaml +```yaml +model_list: + - model_name: multimodalembedding@001 + litellm_params: + model: vertex_ai/multimodalembedding@001 + vertex_project: "adroit-crow-413218" + vertex_location: "us-central1" + vertex_credentials: adroit-crow-413218-a956eef1a2a8.json + +litellm_settings: + drop_params: True +``` + +2. Start Proxy + +``` +$ litellm --config /path/to/config.yaml +``` + +3. Make Request use OpenAI Python SDK, Langchain Python SDK + + +Text + Image + +```python +import openai + +client = openai.OpenAI(api_key="sk-1234", base_url="http://0.0.0.0:4000") + +# # request sent to model set on litellm proxy, `litellm --model` +response = client.embeddings.create( + model="multimodalembedding@001", + input = ["hey", "gs://cloud-samples-data/vertex-ai/llm/prompts/landmark1.png"], +) + +print(response) +``` + +Text + Video +```python +import openai + +client = openai.OpenAI(api_key="sk-1234", base_url="http://0.0.0.0:4000") + +# # request sent to model set on litellm proxy, `litellm --model` +response = client.embeddings.create( + model="multimodalembedding@001", + input = ["hey", "gs://my-bucket/embeddings/supermarket-video.mp4"], +) + +print(response) +``` + +Image + Video +```python +import openai + +client = openai.OpenAI(api_key="sk-1234", base_url="http://0.0.0.0:4000") + +# # request sent to model set on litellm proxy, `litellm --model` +response = client.embeddings.create( + model="multimodalembedding@001", + input = ["gs://cloud-samples-data/vertex-ai/llm/prompts/landmark1.png", "gs://my-bucket/embeddings/supermarket-video.mp4"], +) + +print(response) +``` + + + + + ## **Image Generation Models** Usage diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index 0093464d93..05b3e0be37 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -160,7 +160,7 @@ general_settings: | database_url | string | The URL for the database connection [Set up Virtual Keys](virtual_keys) | | database_connection_pool_limit | integer | The limit for database connection pool [Setting DB Connection Pool limit](#configure-db-pool-limits--connection-timeouts) | | database_connection_timeout | integer | The timeout for database connections in seconds [Setting DB Connection Pool limit, timeout](#configure-db-pool-limits--connection-timeouts) | -| allow_requests_on_db_unavailable | boolean | If true, allows requests to succeed even if DB is unreachable. **Only use this if running LiteLLM in your VPC** This will allow requests to work even when LiteLLM cannot connect to the DB to verify a Virtual Key | +| allow_requests_on_db_unavailable | boolean | If true, allows requests to succeed even if DB is unreachable. **Only use this if running LiteLLM in your VPC** This will allow requests to work even when LiteLLM cannot connect to the DB to verify a Virtual Key [Doc on graceful db unavailability](prod#5-if-running-litellm-on-vpc-gracefully-handle-db-unavailability) | | custom_auth | string | Write your own custom authentication logic [Doc Custom Auth](virtual_keys#custom-auth) | | max_parallel_requests | integer | The max parallel requests allowed per deployment | | global_max_parallel_requests | integer | The max parallel requests allowed on the proxy overall | @@ -515,4 +515,5 @@ router_settings: | UPSTREAM_LANGFUSE_RELEASE | Release version identifier for upstream Langfuse | UPSTREAM_LANGFUSE_SECRET_KEY | Secret key for upstream Langfuse authentication | USE_AWS_KMS | Flag to enable AWS Key Management Service for encryption +| USE_PRISMA_MIGRATE | Flag to use prisma migrate instead of prisma db push. Recommended for production environments. | WEBHOOK_URL | URL for receiving webhooks from external services diff --git a/docs/my-website/docs/proxy/prod.md b/docs/my-website/docs/proxy/prod.md index d3ba2d6224..1c1cbeedb4 100644 --- a/docs/my-website/docs/proxy/prod.md +++ b/docs/my-website/docs/proxy/prod.md @@ -94,15 +94,31 @@ This disables the load_dotenv() functionality, which will automatically load you ## 5. If running LiteLLM on VPC, gracefully handle DB unavailability -This will allow LiteLLM to continue to process requests even if the DB is unavailable. This is better handling for DB unavailability. +When running LiteLLM on a VPC (and inaccessible from the public internet), you can enable graceful degradation so that request processing continues even if the database is temporarily unavailable. + **WARNING: Only do this if you're running LiteLLM on VPC, that cannot be accessed from the public internet.** -```yaml +#### Configuration + +```yaml showLineNumbers title="litellm config.yaml" general_settings: allow_requests_on_db_unavailable: True ``` +#### Expected Behavior + +When `allow_requests_on_db_unavailable` is set to `true`, LiteLLM will handle errors as follows: + +| Type of Error | Expected Behavior | Details | +|---------------|-------------------|----------------| +| Prisma Errors | ✅ Request will be allowed | Covers issues like DB connection resets or rejections from the DB via Prisma, the ORM used by LiteLLM. | +| Httpx Errors | ✅ Request will be allowed | Occurs when the database is unreachable, allowing the request to proceed despite the DB outage. | +| Pod Startup Behavior | ✅ Pods start regardless | LiteLLM Pods will start even if the database is down or unreachable, ensuring higher uptime guarantees for deployments. | +| Health/Readiness Check | ✅ Always returns 200 OK | The /health/readiness endpoint returns a 200 OK status to ensure that pods remain operational even when the database is unavailable. +| LiteLLM Budget Errors or Model Errors | ❌ Request will be blocked | Triggered when the DB is reachable but the authentication token is invalid, lacks access, or exceeds budget limits. | + + ## 6. Disable spend_logs & error_logs if not using the LiteLLM UI By default, LiteLLM writes several types of logs to the database: @@ -182,94 +198,4 @@ You should only see the following level of details in logs on the proxy server # INFO: 192.168.2.205:11774 - "POST /chat/completions HTTP/1.1" 200 OK # INFO: 192.168.2.205:34717 - "POST /chat/completions HTTP/1.1" 200 OK # INFO: 192.168.2.205:29734 - "POST /chat/completions HTTP/1.1" 200 OK -``` - - -### Machine Specifications to Deploy LiteLLM - -| Service | Spec | CPUs | Memory | Architecture | Version| -| --- | --- | --- | --- | --- | --- | -| Server | `t2.small`. | `1vCPUs` | `8GB` | `x86` | -| Redis Cache | - | - | - | - | 7.0+ Redis Engine| - - -### Reference Kubernetes Deployment YAML - -Reference Kubernetes `deployment.yaml` that was load tested by us - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: litellm-deployment -spec: - replicas: 3 - selector: - matchLabels: - app: litellm - template: - metadata: - labels: - app: litellm - spec: - containers: - - name: litellm-container - image: ghcr.io/berriai/litellm:main-latest - imagePullPolicy: Always - env: - - name: AZURE_API_KEY - value: "d6******" - - name: AZURE_API_BASE - value: "https://ope******" - - name: LITELLM_MASTER_KEY - value: "sk-1234" - - name: DATABASE_URL - value: "po**********" - args: - - "--config" - - "/app/proxy_config.yaml" # Update the path to mount the config file - volumeMounts: # Define volume mount for proxy_config.yaml - - name: config-volume - mountPath: /app - readOnly: true - livenessProbe: - httpGet: - path: /health/liveliness - port: 4000 - initialDelaySeconds: 120 - periodSeconds: 15 - successThreshold: 1 - failureThreshold: 3 - timeoutSeconds: 10 - readinessProbe: - httpGet: - path: /health/readiness - port: 4000 - initialDelaySeconds: 120 - periodSeconds: 15 - successThreshold: 1 - failureThreshold: 3 - timeoutSeconds: 10 - volumes: # Define volume to mount proxy_config.yaml - - name: config-volume - configMap: - name: litellm-config - -``` - - -Reference Kubernetes `service.yaml` that was load tested by us -```yaml -apiVersion: v1 -kind: Service -metadata: - name: litellm-service -spec: - selector: - app: litellm - ports: - - protocol: TCP - port: 4000 - targetPort: 4000 - type: LoadBalancer -``` +``` \ No newline at end of file diff --git a/docs/my-website/docs/set_keys.md b/docs/my-website/docs/set_keys.md index 3a5ff08d63..693cf5f7f4 100644 --- a/docs/my-website/docs/set_keys.md +++ b/docs/my-website/docs/set_keys.md @@ -188,7 +188,13 @@ Currently implemented for: - OpenAI (if OPENAI_API_KEY is set) - Fireworks AI (if FIREWORKS_AI_API_KEY is set) - LiteLLM Proxy (if LITELLM_PROXY_API_KEY is set) +- Gemini (if GEMINI_API_KEY is set) +- XAI (if XAI_API_KEY is set) +- Anthropic (if ANTHROPIC_API_KEY is set) +You can also specify a custom provider to check: + +**All providers**: ```python from litellm import get_valid_models @@ -196,6 +202,14 @@ valid_models = get_valid_models(check_provider_endpoint=True) print(valid_models) ``` +**Specific provider**: +```python +from litellm import get_valid_models + +valid_models = get_valid_models(check_provider_endpoint=True, custom_llm_provider="openai") +print(valid_models) +``` + ### `validate_environment(model: str)` This helper tells you if you have all the required environment variables for a model, and if not - what's missing. diff --git a/docs/my-website/docs/tutorials/openweb_ui.md b/docs/my-website/docs/tutorials/openweb_ui.md index ab1e2e121e..b2c1204069 100644 --- a/docs/my-website/docs/tutorials/openweb_ui.md +++ b/docs/my-website/docs/tutorials/openweb_ui.md @@ -98,6 +98,5 @@ On the models dropdown select `thinking-anthropic-claude-3-7-sonnet` - - - +## Additional Resources +- Running LiteLLM and OpenWebUI on Windows Localhost: A Comprehensive Guide [https://www.tanyongsheng.com/note/running-litellm-and-openwebui-on-windows-localhost-a-comprehensive-guide/](https://www.tanyongsheng.com/note/running-litellm-and-openwebui-on-windows-localhost-a-comprehensive-guide/) \ No newline at end of file diff --git a/docs/my-website/img/mcp_2.png b/docs/my-website/img/mcp_2.png new file mode 100644 index 0000000000..98e063efc5 Binary files /dev/null and b/docs/my-website/img/mcp_2.png differ diff --git a/docs/my-website/img/mcp_ui.png b/docs/my-website/img/mcp_ui.png new file mode 100644 index 0000000000..6731fba71b Binary files /dev/null and b/docs/my-website/img/mcp_ui.png differ diff --git a/docs/my-website/img/release_notes/mcp_ui.png b/docs/my-website/img/release_notes/mcp_ui.png new file mode 100644 index 0000000000..8f4cd4ea19 Binary files /dev/null and b/docs/my-website/img/release_notes/mcp_ui.png differ diff --git a/docs/my-website/img/release_notes/team_model_add.png b/docs/my-website/img/release_notes/team_model_add.png new file mode 100644 index 0000000000..f548469846 Binary files /dev/null and b/docs/my-website/img/release_notes/team_model_add.png differ diff --git a/docs/my-website/img/release_notes/ui_usage.png b/docs/my-website/img/release_notes/ui_usage.png new file mode 100644 index 0000000000..ac39ffb918 Binary files /dev/null and b/docs/my-website/img/release_notes/ui_usage.png differ diff --git a/docs/my-website/release_notes/v1.65.0-stable/index.md b/docs/my-website/release_notes/v1.65.0-stable/index.md new file mode 100644 index 0000000000..ec5f3bd441 --- /dev/null +++ b/docs/my-website/release_notes/v1.65.0-stable/index.md @@ -0,0 +1,160 @@ +--- +title: v1.65.0-stable - Model Context Protocol +slug: v1.65.0-stable +date: 2025-03-30T10:00:00 +authors: + - name: Krrish Dholakia + title: CEO, LiteLLM + url: https://www.linkedin.com/in/krish-d/ + image_url: https://media.licdn.com/dms/image/v2/D4D03AQGrlsJ3aqpHmQ/profile-displayphoto-shrink_400_400/B4DZSAzgP7HYAg-/0/1737327772964?e=1743638400&v=beta&t=39KOXMUFedvukiWWVPHf3qI45fuQD7lNglICwN31DrI + - name: Ishaan Jaffer + title: CTO, LiteLLM + url: https://www.linkedin.com/in/reffajnaahsi/ + image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg +tags: [mcp, custom_prompt_management] +hide_table_of_contents: false +--- +import Image from '@theme/IdealImage'; + +v1.65.0-stable is live now. Here are the key highlights of this release: +- **MCP Support**: Support for adding and using MCP servers on the LiteLLM proxy. +- **UI view total usage after 1M+ logs**: You can now view usage analytics after crossing 1M+ logs in DB. + +## Model Context Protocol (MCP) + +This release introduces support for centrally adding MCP servers on LiteLLM. This allows you to add MCP server endpoints and your developers can `list` and `call` MCP tools through LiteLLM. + +Read more about MCP [here](https://docs.litellm.ai/docs/mcp). + + +

+ Expose and use MCP servers through LiteLLM +

+ +## UI view total usage after 1M+ logs + +This release brings the ability to view total usage analytics even after exceeding 1M+ logs in your database. We've implemented a scalable architecture that stores only aggregate usage data, resulting in significantly more efficient queries and reduced database CPU utilization. + + + +

+ View total usage after 1M+ logs +

+ + +- How this works: + - We now aggregate usage data into a dedicated DailyUserSpend table, significantly reducing query load and CPU usage even beyond 1M+ logs. + +- Daily Spend Breakdown API: + + - Retrieve granular daily usage data (by model, provider, and API key) with a single endpoint. + Example Request: + + ```shell title="Daily Spend Breakdown API" showLineNumbers + curl -L -X GET 'http://localhost:4000/user/daily/activity?start_date=2025-03-20&end_date=2025-03-27' \ + -H 'Authorization: Bearer sk-...' + ``` + + ```json title="Daily Spend Breakdown API Response" showLineNumbers + { + "results": [ + { + "date": "2025-03-27", + "metrics": { + "spend": 0.0177072, + "prompt_tokens": 111, + "completion_tokens": 1711, + "total_tokens": 1822, + "api_requests": 11 + }, + "breakdown": { + "models": { + "gpt-4o-mini": { + "spend": 1.095e-05, + "prompt_tokens": 37, + "completion_tokens": 9, + "total_tokens": 46, + "api_requests": 1 + }, + "providers": { "openai": { ... }, "azure_ai": { ... } }, + "api_keys": { "3126b6eaf1...": { ... } } + } + } + ], + "metadata": { + "total_spend": 0.7274667, + "total_prompt_tokens": 280990, + "total_completion_tokens": 376674, + "total_api_requests": 14 + } + } + ``` + + + + +## New Models / Updated Models +- Support for Vertex AI gemini-2.0-flash-lite & Google AI Studio gemini-2.0-flash-lite [PR](https://github.com/BerriAI/litellm/pull/9523) +- Support for Vertex AI Fine-Tuned LLMs [PR](https://github.com/BerriAI/litellm/pull/9542) +- Nova Canvas image generation support [PR](https://github.com/BerriAI/litellm/pull/9525) +- OpenAI gpt-4o-transcribe support [PR](https://github.com/BerriAI/litellm/pull/9517) +- Added new Vertex AI text embedding model [PR](https://github.com/BerriAI/litellm/pull/9476) + +## LLM Translation +- OpenAI Web Search Tool Call Support [PR](https://github.com/BerriAI/litellm/pull/9465) +- Vertex AI topLogprobs support [PR](https://github.com/BerriAI/litellm/pull/9518) +- Support for sending images and video to Vertex AI multimodal embedding [Doc](https://docs.litellm.ai/docs/providers/vertex#multi-modal-embeddings) +- Support litellm.api_base for Vertex AI + Gemini across completion, embedding, image_generation [PR](https://github.com/BerriAI/litellm/pull/9516) +- Bug fix for returning `response_cost` when using litellm python SDK with LiteLLM Proxy [PR](https://github.com/BerriAI/litellm/commit/6fd18651d129d606182ff4b980e95768fc43ca3d) +- Support for `max_completion_tokens` on Mistral API [PR](https://github.com/BerriAI/litellm/pull/9606) +- Refactored Vertex AI passthrough routes - fixes unpredictable behaviour with auto-setting default_vertex_region on router model add [PR](https://github.com/BerriAI/litellm/pull/9467) + +## Spend Tracking Improvements +- Log 'api_base' on spend logs [PR](https://github.com/BerriAI/litellm/pull/9509) +- Support for Gemini audio token cost tracking [PR](https://github.com/BerriAI/litellm/pull/9535) +- Fixed OpenAI audio input token cost tracking [PR](https://github.com/BerriAI/litellm/pull/9535) + +## UI + +### Model Management +- Allowed team admins to add/update/delete models on UI [PR](https://github.com/BerriAI/litellm/pull/9572) +- Added render supports_web_search on model hub [PR](https://github.com/BerriAI/litellm/pull/9469) + +### Request Logs +- Show API base and model ID on request logs [PR](https://github.com/BerriAI/litellm/pull/9572) +- Allow viewing keyinfo on request logs [PR](https://github.com/BerriAI/litellm/pull/9568) + +### Usage Tab +- Added Daily User Spend Aggregate view - allows UI Usage tab to work > 1m rows [PR](https://github.com/BerriAI/litellm/pull/9538) +- Connected UI to "LiteLLM_DailyUserSpend" spend table [PR](https://github.com/BerriAI/litellm/pull/9603) + +## Logging Integrations +- Fixed StandardLoggingPayload for GCS Pub Sub Logging Integration [PR](https://github.com/BerriAI/litellm/pull/9508) +- Track `litellm_model_name` on `StandardLoggingPayload` [Docs](https://docs.litellm.ai/docs/proxy/logging_spec#standardlogginghiddenparams) + +## Performance / Reliability Improvements +- LiteLLM Redis semantic caching implementation [PR](https://github.com/BerriAI/litellm/pull/9356) +- Gracefully handle exceptions when DB is having an outage [PR](https://github.com/BerriAI/litellm/pull/9533) +- Allow Pods to startup + passing /health/readiness when allow_requests_on_db_unavailable: True and DB is down [PR](https://github.com/BerriAI/litellm/pull/9569) + + +## General Improvements +- Support for exposing MCP tools on litellm proxy [PR](https://github.com/BerriAI/litellm/pull/9426) +- Support discovering Gemini, Anthropic, xAI models by calling their /v1/model endpoint [PR](https://github.com/BerriAI/litellm/pull/9530) +- Fixed route check for non-proxy admins on JWT auth [PR](https://github.com/BerriAI/litellm/pull/9454) +- Added baseline Prisma database migrations [PR](https://github.com/BerriAI/litellm/pull/9565) +- View all wildcard models on /model/info [PR](https://github.com/BerriAI/litellm/pull/9572) + + +## Security +- Bumped next from 14.2.21 to 14.2.25 in UI dashboard [PR](https://github.com/BerriAI/litellm/pull/9458) + +## Complete Git Diff + +[Here's the complete git diff](https://github.com/BerriAI/litellm/compare/v1.63.14-stable.patch1...v1.65.0-stable) diff --git a/docs/my-website/release_notes/v1.65.0/index.md b/docs/my-website/release_notes/v1.65.0/index.md new file mode 100644 index 0000000000..46525ea55f --- /dev/null +++ b/docs/my-website/release_notes/v1.65.0/index.md @@ -0,0 +1,34 @@ +--- +title: v1.65.0 - Team Model Add - update +slug: v1.65.0 +date: 2025-03-28T10:00:00 +authors: + - name: Krrish Dholakia + title: CEO, LiteLLM + url: https://www.linkedin.com/in/krish-d/ + image_url: https://media.licdn.com/dms/image/v2/D4D03AQGrlsJ3aqpHmQ/profile-displayphoto-shrink_400_400/B4DZSAzgP7HYAg-/0/1737327772964?e=1743638400&v=beta&t=39KOXMUFedvukiWWVPHf3qI45fuQD7lNglICwN31DrI + - name: Ishaan Jaffer + title: CTO, LiteLLM + url: https://www.linkedin.com/in/reffajnaahsi/ + image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg +tags: [management endpoints, team models, ui] +hide_table_of_contents: false +--- + +import Image from '@theme/IdealImage'; + +v1.65.0 updates the `/model/new` endpoint to prevent non-team admins from creating team models. + +This means that only proxy admins or team admins can create team models. + +## Additional Changes + +- Allows team admins to call `/model/update` to update team models. +- Allows team admins to call `/model/delete` to delete team models. +- Introduces new `user_models_only` param to `/v2/model/info` - only return models added by this user. + + +These changes enable team admins to add and manage models for their team on the LiteLLM UI + API. + + + \ No newline at end of file diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index 2e5d0f2a0a..5aeee715d1 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -304,7 +304,6 @@ const sidebars = { "image_variations", ] }, - "mcp", { type: "category", label: "/audio", diff --git a/enterprise/enterprise_hooks/secret_detection.py b/enterprise/enterprise_hooks/secret_detection.py index 459fd374d1..158f26efa3 100644 --- a/enterprise/enterprise_hooks/secret_detection.py +++ b/enterprise/enterprise_hooks/secret_detection.py @@ -444,9 +444,7 @@ class _ENTERPRISE_SecretDetection(CustomGuardrail): detected_secrets = [] for file in secrets.files: - for found_secret in secrets[file]: - if found_secret.secret_value is None: continue detected_secrets.append( @@ -471,14 +469,12 @@ class _ENTERPRISE_SecretDetection(CustomGuardrail): data: dict, call_type: str, # "completion", "embeddings", "image_generation", "moderation" ): - if await self.should_run_check(user_api_key_dict) is False: return if "messages" in data and isinstance(data["messages"], list): for message in data["messages"]: if "content" in message and isinstance(message["content"], str): - detected_secrets = self.scan_message_for_secrets(message["content"]) for secret in detected_secrets: diff --git a/litellm-proxy-extras/LICENSE b/litellm-proxy-extras/LICENSE new file mode 100644 index 0000000000..3bfef5bae9 --- /dev/null +++ b/litellm-proxy-extras/LICENSE @@ -0,0 +1,26 @@ +Portions of this software are licensed as follows: + +* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE". +* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below. +--- +MIT License + +Copyright (c) 2023 Berri AI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/litellm-proxy-extras/README.md b/litellm-proxy-extras/README.md new file mode 100644 index 0000000000..29453f65ba --- /dev/null +++ b/litellm-proxy-extras/README.md @@ -0,0 +1,21 @@ +Additional files for the proxy. Reduces the size of the main litellm package. + +Currently, only stores the migration.sql files for litellm-proxy. + +To install, run: + +```bash +pip install litellm-proxy-extras +``` +OR + +```bash +pip install litellm[proxy] # installs litellm-proxy-extras and other proxy dependencies. +``` + +To use the migrations, run: + +```bash +litellm --use_prisma_migrate +``` + diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl new file mode 100644 index 0000000000..1aff64ef58 Binary files /dev/null and b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0-py3-none-any.whl differ diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0.tar.gz b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0.tar.gz new file mode 100644 index 0000000000..0bdf828163 Binary files /dev/null and b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.0.tar.gz differ diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl new file mode 100644 index 0000000000..e2583935a4 Binary files /dev/null and b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1-py3-none-any.whl differ diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1.tar.gz b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1.tar.gz new file mode 100644 index 0000000000..c9111dd9c3 Binary files /dev/null and b/litellm-proxy-extras/dist/litellm_proxy_extras-0.1.1.tar.gz differ diff --git a/litellm-proxy-extras/litellm_proxy_extras/__init__.py b/litellm-proxy-extras/litellm_proxy_extras/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/litellm-proxy-extras/litellm_proxy_extras/_logging.py b/litellm-proxy-extras/litellm_proxy_extras/_logging.py new file mode 100644 index 0000000000..118caecf48 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/_logging.py @@ -0,0 +1,12 @@ +import logging + +# Set up package logger +logger = logging.getLogger("litellm_proxy_extras") +if not logger.handlers: # Only add handler if none exists + handler = logging.StreamHandler() + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + handler.setFormatter(formatter) + logger.addHandler(handler) + logger.setLevel(logging.INFO) diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326162113_baseline/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326162113_baseline/migration.sql new file mode 100644 index 0000000000..fb8a44814f --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326162113_baseline/migration.sql @@ -0,0 +1,360 @@ +-- CreateTable +CREATE TABLE "LiteLLM_BudgetTable" ( + "budget_id" TEXT NOT NULL, + "max_budget" DOUBLE PRECISION, + "soft_budget" DOUBLE PRECISION, + "max_parallel_requests" INTEGER, + "tpm_limit" BIGINT, + "rpm_limit" BIGINT, + "model_max_budget" JSONB, + "budget_duration" TEXT, + "budget_reset_at" TIMESTAMP(3), + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_BudgetTable_pkey" PRIMARY KEY ("budget_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_CredentialsTable" ( + "credential_id" TEXT NOT NULL, + "credential_name" TEXT NOT NULL, + "credential_values" JSONB NOT NULL, + "credential_info" JSONB, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_CredentialsTable_pkey" PRIMARY KEY ("credential_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_ProxyModelTable" ( + "model_id" TEXT NOT NULL, + "model_name" TEXT NOT NULL, + "litellm_params" JSONB NOT NULL, + "model_info" JSONB, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_ProxyModelTable_pkey" PRIMARY KEY ("model_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_OrganizationTable" ( + "organization_id" TEXT NOT NULL, + "organization_alias" TEXT NOT NULL, + "budget_id" TEXT NOT NULL, + "metadata" JSONB NOT NULL DEFAULT '{}', + "models" TEXT[], + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "model_spend" JSONB NOT NULL DEFAULT '{}', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_OrganizationTable_pkey" PRIMARY KEY ("organization_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_ModelTable" ( + "id" SERIAL NOT NULL, + "aliases" JSONB, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_ModelTable_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_TeamTable" ( + "team_id" TEXT NOT NULL, + "team_alias" TEXT, + "organization_id" TEXT, + "admins" TEXT[], + "members" TEXT[], + "members_with_roles" JSONB NOT NULL DEFAULT '{}', + "metadata" JSONB NOT NULL DEFAULT '{}', + "max_budget" DOUBLE PRECISION, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "models" TEXT[], + "max_parallel_requests" INTEGER, + "tpm_limit" BIGINT, + "rpm_limit" BIGINT, + "budget_duration" TEXT, + "budget_reset_at" TIMESTAMP(3), + "blocked" BOOLEAN NOT NULL DEFAULT false, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "model_spend" JSONB NOT NULL DEFAULT '{}', + "model_max_budget" JSONB NOT NULL DEFAULT '{}', + "model_id" INTEGER, + + CONSTRAINT "LiteLLM_TeamTable_pkey" PRIMARY KEY ("team_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_UserTable" ( + "user_id" TEXT NOT NULL, + "user_alias" TEXT, + "team_id" TEXT, + "sso_user_id" TEXT, + "organization_id" TEXT, + "password" TEXT, + "teams" TEXT[] DEFAULT ARRAY[]::TEXT[], + "user_role" TEXT, + "max_budget" DOUBLE PRECISION, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "user_email" TEXT, + "models" TEXT[], + "metadata" JSONB NOT NULL DEFAULT '{}', + "max_parallel_requests" INTEGER, + "tpm_limit" BIGINT, + "rpm_limit" BIGINT, + "budget_duration" TEXT, + "budget_reset_at" TIMESTAMP(3), + "allowed_cache_controls" TEXT[] DEFAULT ARRAY[]::TEXT[], + "model_spend" JSONB NOT NULL DEFAULT '{}', + "model_max_budget" JSONB NOT NULL DEFAULT '{}', + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "LiteLLM_UserTable_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_VerificationToken" ( + "token" TEXT NOT NULL, + "key_name" TEXT, + "key_alias" TEXT, + "soft_budget_cooldown" BOOLEAN NOT NULL DEFAULT false, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "expires" TIMESTAMP(3), + "models" TEXT[], + "aliases" JSONB NOT NULL DEFAULT '{}', + "config" JSONB NOT NULL DEFAULT '{}', + "user_id" TEXT, + "team_id" TEXT, + "permissions" JSONB NOT NULL DEFAULT '{}', + "max_parallel_requests" INTEGER, + "metadata" JSONB NOT NULL DEFAULT '{}', + "blocked" BOOLEAN, + "tpm_limit" BIGINT, + "rpm_limit" BIGINT, + "max_budget" DOUBLE PRECISION, + "budget_duration" TEXT, + "budget_reset_at" TIMESTAMP(3), + "allowed_cache_controls" TEXT[] DEFAULT ARRAY[]::TEXT[], + "model_spend" JSONB NOT NULL DEFAULT '{}', + "model_max_budget" JSONB NOT NULL DEFAULT '{}', + "budget_id" TEXT, + "organization_id" TEXT, + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "created_by" TEXT, + "updated_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_by" TEXT, + + CONSTRAINT "LiteLLM_VerificationToken_pkey" PRIMARY KEY ("token") +); + +-- CreateTable +CREATE TABLE "LiteLLM_EndUserTable" ( + "user_id" TEXT NOT NULL, + "alias" TEXT, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "allowed_model_region" TEXT, + "default_model" TEXT, + "budget_id" TEXT, + "blocked" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "LiteLLM_EndUserTable_pkey" PRIMARY KEY ("user_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_Config" ( + "param_name" TEXT NOT NULL, + "param_value" JSONB, + + CONSTRAINT "LiteLLM_Config_pkey" PRIMARY KEY ("param_name") +); + +-- CreateTable +CREATE TABLE "LiteLLM_SpendLogs" ( + "request_id" TEXT NOT NULL, + "call_type" TEXT NOT NULL, + "api_key" TEXT NOT NULL DEFAULT '', + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "total_tokens" INTEGER NOT NULL DEFAULT 0, + "prompt_tokens" INTEGER NOT NULL DEFAULT 0, + "completion_tokens" INTEGER NOT NULL DEFAULT 0, + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + "completionStartTime" TIMESTAMP(3), + "model" TEXT NOT NULL DEFAULT '', + "model_id" TEXT DEFAULT '', + "model_group" TEXT DEFAULT '', + "custom_llm_provider" TEXT DEFAULT '', + "api_base" TEXT DEFAULT '', + "user" TEXT DEFAULT '', + "metadata" JSONB DEFAULT '{}', + "cache_hit" TEXT DEFAULT '', + "cache_key" TEXT DEFAULT '', + "request_tags" JSONB DEFAULT '[]', + "team_id" TEXT, + "end_user" TEXT, + "requester_ip_address" TEXT, + "messages" JSONB DEFAULT '{}', + "response" JSONB DEFAULT '{}', + + CONSTRAINT "LiteLLM_SpendLogs_pkey" PRIMARY KEY ("request_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_ErrorLogs" ( + "request_id" TEXT NOT NULL, + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + "api_base" TEXT NOT NULL DEFAULT '', + "model_group" TEXT NOT NULL DEFAULT '', + "litellm_model_name" TEXT NOT NULL DEFAULT '', + "model_id" TEXT NOT NULL DEFAULT '', + "request_kwargs" JSONB NOT NULL DEFAULT '{}', + "exception_type" TEXT NOT NULL DEFAULT '', + "exception_string" TEXT NOT NULL DEFAULT '', + "status_code" TEXT NOT NULL DEFAULT '', + + CONSTRAINT "LiteLLM_ErrorLogs_pkey" PRIMARY KEY ("request_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_UserNotifications" ( + "request_id" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "models" TEXT[], + "justification" TEXT NOT NULL, + "status" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_UserNotifications_pkey" PRIMARY KEY ("request_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_TeamMembership" ( + "user_id" TEXT NOT NULL, + "team_id" TEXT NOT NULL, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "budget_id" TEXT, + + CONSTRAINT "LiteLLM_TeamMembership_pkey" PRIMARY KEY ("user_id","team_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_OrganizationMembership" ( + "user_id" TEXT NOT NULL, + "organization_id" TEXT NOT NULL, + "user_role" TEXT, + "spend" DOUBLE PRECISION DEFAULT 0.0, + "budget_id" TEXT, + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "LiteLLM_OrganizationMembership_pkey" PRIMARY KEY ("user_id","organization_id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_InvitationLink" ( + "id" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "is_accepted" BOOLEAN NOT NULL DEFAULT false, + "accepted_at" TIMESTAMP(3), + "expires_at" TIMESTAMP(3) NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL, + "created_by" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL, + "updated_by" TEXT NOT NULL, + + CONSTRAINT "LiteLLM_InvitationLink_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "LiteLLM_AuditLog" ( + "id" TEXT NOT NULL, + "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "changed_by" TEXT NOT NULL DEFAULT '', + "changed_by_api_key" TEXT NOT NULL DEFAULT '', + "action" TEXT NOT NULL, + "table_name" TEXT NOT NULL, + "object_id" TEXT NOT NULL, + "before_value" JSONB, + "updated_values" JSONB, + + CONSTRAINT "LiteLLM_AuditLog_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_CredentialsTable_credential_name_key" ON "LiteLLM_CredentialsTable"("credential_name"); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_TeamTable_model_id_key" ON "LiteLLM_TeamTable"("model_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_UserTable_sso_user_id_key" ON "LiteLLM_UserTable"("sso_user_id"); + +-- CreateIndex +CREATE INDEX "LiteLLM_SpendLogs_startTime_idx" ON "LiteLLM_SpendLogs"("startTime"); + +-- CreateIndex +CREATE INDEX "LiteLLM_SpendLogs_end_user_idx" ON "LiteLLM_SpendLogs"("end_user"); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_OrganizationMembership_user_id_organization_id_key" ON "LiteLLM_OrganizationMembership"("user_id", "organization_id"); + +-- AddForeignKey +ALTER TABLE "LiteLLM_OrganizationTable" ADD CONSTRAINT "LiteLLM_OrganizationTable_budget_id_fkey" FOREIGN KEY ("budget_id") REFERENCES "LiteLLM_BudgetTable"("budget_id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_TeamTable" ADD CONSTRAINT "LiteLLM_TeamTable_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "LiteLLM_OrganizationTable"("organization_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_TeamTable" ADD CONSTRAINT "LiteLLM_TeamTable_model_id_fkey" FOREIGN KEY ("model_id") REFERENCES "LiteLLM_ModelTable"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_UserTable" ADD CONSTRAINT "LiteLLM_UserTable_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "LiteLLM_OrganizationTable"("organization_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_VerificationToken" ADD CONSTRAINT "LiteLLM_VerificationToken_budget_id_fkey" FOREIGN KEY ("budget_id") REFERENCES "LiteLLM_BudgetTable"("budget_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_VerificationToken" ADD CONSTRAINT "LiteLLM_VerificationToken_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "LiteLLM_OrganizationTable"("organization_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_EndUserTable" ADD CONSTRAINT "LiteLLM_EndUserTable_budget_id_fkey" FOREIGN KEY ("budget_id") REFERENCES "LiteLLM_BudgetTable"("budget_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_TeamMembership" ADD CONSTRAINT "LiteLLM_TeamMembership_budget_id_fkey" FOREIGN KEY ("budget_id") REFERENCES "LiteLLM_BudgetTable"("budget_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_OrganizationMembership" ADD CONSTRAINT "LiteLLM_OrganizationMembership_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "LiteLLM_UserTable"("user_id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_OrganizationMembership" ADD CONSTRAINT "LiteLLM_OrganizationMembership_organization_id_fkey" FOREIGN KEY ("organization_id") REFERENCES "LiteLLM_OrganizationTable"("organization_id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_OrganizationMembership" ADD CONSTRAINT "LiteLLM_OrganizationMembership_budget_id_fkey" FOREIGN KEY ("budget_id") REFERENCES "LiteLLM_BudgetTable"("budget_id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_InvitationLink" ADD CONSTRAINT "LiteLLM_InvitationLink_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "LiteLLM_UserTable"("user_id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_InvitationLink" ADD CONSTRAINT "LiteLLM_InvitationLink_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "LiteLLM_UserTable"("user_id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "LiteLLM_InvitationLink" ADD CONSTRAINT "LiteLLM_InvitationLink_updated_by_fkey" FOREIGN KEY ("updated_by") REFERENCES "LiteLLM_UserTable"("user_id") ON DELETE RESTRICT ON UPDATE CASCADE; + diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326171002_add_daily_user_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326171002_add_daily_user_table/migration.sql new file mode 100644 index 0000000000..3379d8e9fd --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250326171002_add_daily_user_table/migration.sql @@ -0,0 +1,33 @@ +-- CreateTable +CREATE TABLE "LiteLLM_DailyUserSpend" ( + "id" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "date" TEXT NOT NULL, + "api_key" TEXT NOT NULL, + "model" TEXT NOT NULL, + "model_group" TEXT, + "custom_llm_provider" TEXT, + "prompt_tokens" INTEGER NOT NULL DEFAULT 0, + "completion_tokens" INTEGER NOT NULL DEFAULT 0, + "spend" DOUBLE PRECISION NOT NULL DEFAULT 0.0, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "LiteLLM_DailyUserSpend_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "LiteLLM_DailyUserSpend_date_idx" ON "LiteLLM_DailyUserSpend"("date"); + +-- CreateIndex +CREATE INDEX "LiteLLM_DailyUserSpend_user_id_idx" ON "LiteLLM_DailyUserSpend"("user_id"); + +-- CreateIndex +CREATE INDEX "LiteLLM_DailyUserSpend_api_key_idx" ON "LiteLLM_DailyUserSpend"("api_key"); + +-- CreateIndex +CREATE INDEX "LiteLLM_DailyUserSpend_model_idx" ON "LiteLLM_DailyUserSpend"("model"); + +-- CreateIndex +CREATE UNIQUE INDEX "LiteLLM_DailyUserSpend_user_id_date_api_key_model_custom_ll_key" ON "LiteLLM_DailyUserSpend"("user_id", "date", "api_key", "model", "custom_llm_provider"); + diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql new file mode 100644 index 0000000000..e7c5ab566a --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250327180120_add_api_requests_to_daily_user_table/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "LiteLLM_DailyUserSpend" ADD COLUMN "api_requests" INTEGER NOT NULL DEFAULT 0; + diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20250329084805_new_cron_job_table/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250329084805_new_cron_job_table/migration.sql new file mode 100644 index 0000000000..e7ea2e9015 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20250329084805_new_cron_job_table/migration.sql @@ -0,0 +1,14 @@ +-- CreateEnum +CREATE TYPE "JobStatus" AS ENUM ('ACTIVE', 'INACTIVE'); + +-- CreateTable +CREATE TABLE "LiteLLM_CronJob" ( + "cronjob_id" TEXT NOT NULL, + "pod_id" TEXT NOT NULL, + "status" "JobStatus" NOT NULL DEFAULT 'INACTIVE', + "last_updated" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "ttl" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "LiteLLM_CronJob_pkey" PRIMARY KEY ("cronjob_id") +); + diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/migration_lock.toml b/litellm-proxy-extras/litellm_proxy_extras/migrations/migration_lock.toml new file mode 100644 index 0000000000..2fe25d87cc --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/migration_lock.toml @@ -0,0 +1 @@ +provider = "postgresql" diff --git a/litellm-proxy-extras/litellm_proxy_extras/utils.py b/litellm-proxy-extras/litellm_proxy_extras/utils.py new file mode 100644 index 0000000000..894ae34122 --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/utils.py @@ -0,0 +1,80 @@ +import os +import random +import subprocess +import time +from typing import Optional + +from litellm_proxy_extras._logging import logger + + +def str_to_bool(value: Optional[str]) -> bool: + if value is None: + return False + return value.lower() in ("true", "1", "t", "y", "yes") + + +class ProxyExtrasDBManager: + @staticmethod + def setup_database(schema_path: str, use_migrate: bool = False) -> bool: + """ + Set up the database using either prisma migrate or prisma db push + Uses migrations from litellm-proxy-extras package + + Args: + schema_path (str): Path to the Prisma schema file + use_migrate (bool): Whether to use prisma migrate instead of db push + + Returns: + bool: True if setup was successful, False otherwise + """ + use_migrate = str_to_bool(os.getenv("USE_PRISMA_MIGRATE")) or use_migrate + for attempt in range(4): + original_dir = os.getcwd() + schema_dir = os.path.dirname(schema_path) + os.chdir(schema_dir) + + try: + if use_migrate: + logger.info("Running prisma migrate deploy") + try: + # Set migrations directory for Prisma + subprocess.run( + ["prisma", "migrate", "deploy"], + timeout=60, + check=True, + capture_output=True, + text=True, + ) + logger.info("prisma migrate deploy completed") + return True + except subprocess.CalledProcessError as e: + logger.info(f"prisma db error: {e.stderr}, e: {e.stdout}") + if ( + "P3005" in e.stderr + and "database schema is not empty" in e.stderr + ): + logger.info("Error: Database schema is not empty") + return False + else: + # Use prisma db push with increased timeout + subprocess.run( + ["prisma", "db", "push", "--accept-data-loss"], + timeout=60, + check=True, + ) + return True + except subprocess.TimeoutExpired: + logger.info(f"Attempt {attempt + 1} timed out") + time.sleep(random.randrange(5, 15)) + except subprocess.CalledProcessError as e: + attempts_left = 3 - attempt + retry_msg = ( + f" Retrying... ({attempts_left} attempts left)" + if attempts_left > 0 + else "" + ) + logger.info(f"The process failed to execute. Details: {e}.{retry_msg}") + time.sleep(random.randrange(5, 15)) + finally: + os.chdir(original_dir) + return False diff --git a/litellm-proxy-extras/pyproject.toml b/litellm-proxy-extras/pyproject.toml new file mode 100644 index 0000000000..c130a7fa9b --- /dev/null +++ b/litellm-proxy-extras/pyproject.toml @@ -0,0 +1,30 @@ +[tool.poetry] +name = "litellm-proxy-extras" +version = "0.1.1" +description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." +authors = ["BerriAI"] +readme = "README.md" + + +[tool.poetry.urls] +homepage = "https://litellm.ai" +Homepage = "https://litellm.ai" +repository = "https://github.com/BerriAI/litellm" +Repository = "https://github.com/BerriAI/litellm" +documentation = "https://docs.litellm.ai" +Documentation = "https://docs.litellm.ai" + +[tool.poetry.dependencies] +python = ">=3.8.1,<4.0, !=3.9.7" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + +[tool.commitizen] +version = "0.1.1" +version_files = [ + "pyproject.toml:version", + "../requirements.txt:litellm-proxy-extras==", + "../pyproject.toml:litellm-proxy-extras = {version = \"" +] \ No newline at end of file diff --git a/litellm-proxy-extras/tests/__init__.py b/litellm-proxy-extras/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/litellm/__init__.py b/litellm/__init__.py index 4e1410a319..a62322cef9 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -122,19 +122,19 @@ langsmith_batch_size: Optional[int] = None prometheus_initialize_budget_metrics: Optional[bool] = False argilla_batch_size: Optional[int] = None datadog_use_v1: Optional[bool] = False # if you want to use v1 datadog logged payload -gcs_pub_sub_use_v1: Optional[bool] = ( - False # if you want to use v1 gcs pubsub logged payload -) +gcs_pub_sub_use_v1: Optional[ + bool +] = False # if you want to use v1 gcs pubsub logged payload argilla_transformation_object: Optional[Dict[str, Any]] = None -_async_input_callback: List[Union[str, Callable, CustomLogger]] = ( - [] -) # internal variable - async custom callbacks are routed here. -_async_success_callback: List[Union[str, Callable, CustomLogger]] = ( - [] -) # internal variable - async custom callbacks are routed here. -_async_failure_callback: List[Union[str, Callable, CustomLogger]] = ( - [] -) # internal variable - async custom callbacks are routed here. +_async_input_callback: List[ + Union[str, Callable, CustomLogger] +] = [] # internal variable - async custom callbacks are routed here. +_async_success_callback: List[ + Union[str, Callable, CustomLogger] +] = [] # internal variable - async custom callbacks are routed here. +_async_failure_callback: List[ + Union[str, Callable, CustomLogger] +] = [] # internal variable - async custom callbacks are routed here. pre_call_rules: List[Callable] = [] post_call_rules: List[Callable] = [] turn_off_message_logging: Optional[bool] = False @@ -142,18 +142,18 @@ log_raw_request_response: bool = False redact_messages_in_exceptions: Optional[bool] = False redact_user_api_key_info: Optional[bool] = False filter_invalid_headers: Optional[bool] = False -add_user_information_to_llm_headers: Optional[bool] = ( - None # adds user_id, team_id, token hash (params from StandardLoggingMetadata) to request headers -) +add_user_information_to_llm_headers: Optional[ + bool +] = None # adds user_id, team_id, token hash (params from StandardLoggingMetadata) to request headers store_audit_logs = False # Enterprise feature, allow users to see audit logs ### end of callbacks ############# -email: Optional[str] = ( - None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -) -token: Optional[str] = ( - None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -) +email: Optional[ + str +] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +token: Optional[ + str +] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 telemetry = True max_tokens = 256 # OpenAI Defaults drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False)) @@ -229,24 +229,20 @@ enable_loadbalancing_on_batch_endpoints: Optional[bool] = None enable_caching_on_provider_specific_optional_params: bool = ( False # feature-flag for caching on optional params - e.g. 'top_k' ) -caching: bool = ( - False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -) -caching_with_models: bool = ( - False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 -) -cache: Optional[Cache] = ( - None # cache object <- use this - https://docs.litellm.ai/docs/caching -) +caching: bool = False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +caching_with_models: bool = False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648 +cache: Optional[ + Cache +] = None # cache object <- use this - https://docs.litellm.ai/docs/caching default_in_memory_ttl: Optional[float] = None default_redis_ttl: Optional[float] = None default_redis_batch_cache_expiry: Optional[float] = None model_alias_map: Dict[str, str] = {} model_group_alias_map: Dict[str, str] = {} max_budget: float = 0.0 # set the max budget across all providers -budget_duration: Optional[str] = ( - None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). -) +budget_duration: Optional[ + str +] = None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d"). default_soft_budget: float = ( 50.0 # by default all litellm proxy keys have a soft budget of 50.0 ) @@ -255,15 +251,11 @@ forward_traceparent_to_llm_provider: bool = False _current_cost = 0.0 # private variable, used if max budget is set error_logs: Dict = {} -add_function_to_prompt: bool = ( - False # if function calling not supported by api, append function call details to system prompt -) +add_function_to_prompt: bool = False # if function calling not supported by api, append function call details to system prompt client_session: Optional[httpx.Client] = None aclient_session: Optional[httpx.AsyncClient] = None model_fallbacks: Optional[List] = None # Deprecated for 'litellm.fallbacks' -model_cost_map_url: str = ( - "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json" -) +model_cost_map_url: str = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json" suppress_debug_info = False dynamodb_table_name: Optional[str] = None s3_callback_params: Optional[Dict] = None @@ -285,9 +277,7 @@ disable_end_user_cost_tracking_prometheus_only: Optional[bool] = None custom_prometheus_metadata_labels: List[str] = [] #### REQUEST PRIORITIZATION #### priority_reservation: Optional[Dict[str, float]] = None -force_ipv4: bool = ( - False # when True, litellm will force ipv4 for all LLM requests. Some users have seen httpx ConnectionError when using ipv6. -) +force_ipv4: bool = False # when True, litellm will force ipv4 for all LLM requests. Some users have seen httpx ConnectionError when using ipv6. module_level_aclient = AsyncHTTPHandler( timeout=request_timeout, client_alias="module level aclient" ) @@ -301,13 +291,13 @@ fallbacks: Optional[List] = None context_window_fallbacks: Optional[List] = None content_policy_fallbacks: Optional[List] = None allowed_fails: int = 3 -num_retries_per_request: Optional[int] = ( - None # for the request overall (incl. fallbacks + model retries) -) +num_retries_per_request: Optional[ + int +] = None # for the request overall (incl. fallbacks + model retries) ####### SECRET MANAGERS ##################### -secret_manager_client: Optional[Any] = ( - None # list of instantiated key management clients - e.g. azure kv, infisical, etc. -) +secret_manager_client: Optional[ + Any +] = None # list of instantiated key management clients - e.g. azure kv, infisical, etc. _google_kms_resource_name: Optional[str] = None _key_management_system: Optional[KeyManagementSystem] = None _key_management_settings: KeyManagementSettings = KeyManagementSettings() @@ -813,6 +803,7 @@ from .llms.oobabooga.chat.transformation import OobaboogaConfig from .llms.maritalk import MaritalkConfig from .llms.openrouter.chat.transformation import OpenrouterConfig from .llms.anthropic.chat.transformation import AnthropicConfig +from .llms.anthropic.common_utils import AnthropicModelInfo from .llms.groq.stt.transformation import GroqSTTConfig from .llms.anthropic.completion.transformation import AnthropicTextConfig from .llms.triton.completion.transformation import TritonConfig @@ -848,6 +839,7 @@ from .llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( VertexGeminiConfig, VertexGeminiConfig as VertexAIConfig, ) +from .llms.gemini.common_utils import GeminiModelInfo from .llms.gemini.chat.transformation import ( GoogleAIStudioGeminiConfig, GoogleAIStudioGeminiConfig as GeminiConfig, # aliased to maintain backwards compatibility @@ -950,6 +942,12 @@ openaiOSeriesConfig = OpenAIOSeriesConfig() from .llms.openai.chat.gpt_transformation import ( OpenAIGPTConfig, ) +from .llms.openai.transcriptions.whisper_transformation import ( + OpenAIWhisperAudioTranscriptionConfig, +) +from .llms.openai.transcriptions.gpt_transformation import ( + OpenAIGPTAudioTranscriptionConfig, +) openAIGPTConfig = OpenAIGPTConfig() from .llms.openai.chat.gpt_audio_transformation import ( @@ -978,6 +976,7 @@ from .llms.fireworks_ai.embed.fireworks_ai_transformation import ( from .llms.friendliai.chat.transformation import FriendliaiChatConfig from .llms.jina_ai.embedding.transformation import JinaAIEmbeddingConfig from .llms.xai.chat.transformation import XAIChatConfig +from .llms.xai.common_utils import XAIModelInfo from .llms.volcengine import VolcEngineConfig from .llms.codestral.completion.transformation import CodestralTextCompletionConfig from .llms.azure.azure import ( @@ -1048,10 +1047,10 @@ from .types.llms.custom_llm import CustomLLMItem from .types.utils import GenericStreamingChunk custom_provider_map: List[CustomLLMItem] = [] -_custom_providers: List[str] = ( - [] -) # internal helper util, used to track names of custom providers -disable_hf_tokenizer_download: Optional[bool] = ( - None # disable huggingface tokenizer download. Defaults to openai clk100 -) +_custom_providers: List[ + str +] = [] # internal helper util, used to track names of custom providers +disable_hf_tokenizer_download: Optional[ + bool +] = None # disable huggingface tokenizer download. Defaults to openai clk100 global_disable_no_log_param: bool = False diff --git a/litellm/_logging.py b/litellm/_logging.py index 151ae6003d..d7e2c9e778 100644 --- a/litellm/_logging.py +++ b/litellm/_logging.py @@ -1,6 +1,7 @@ import json import logging import os +import sys from datetime import datetime from logging import Formatter @@ -40,9 +41,56 @@ class JsonFormatter(Formatter): return json.dumps(json_record) +# Function to set up exception handlers for JSON logging +def _setup_json_exception_handlers(formatter): + # Create a handler with JSON formatting for exceptions + error_handler = logging.StreamHandler() + error_handler.setFormatter(formatter) + + # Setup excepthook for uncaught exceptions + def json_excepthook(exc_type, exc_value, exc_traceback): + record = logging.LogRecord( + name="LiteLLM", + level=logging.ERROR, + pathname="", + lineno=0, + msg=str(exc_value), + args=(), + exc_info=(exc_type, exc_value, exc_traceback), + ) + error_handler.handle(record) + + sys.excepthook = json_excepthook + + # Configure asyncio exception handler if possible + try: + import asyncio + + def async_json_exception_handler(loop, context): + exception = context.get("exception") + if exception: + record = logging.LogRecord( + name="LiteLLM", + level=logging.ERROR, + pathname="", + lineno=0, + msg=str(exception), + args=(), + exc_info=None, + ) + error_handler.handle(record) + else: + loop.default_exception_handler(context) + + asyncio.get_event_loop().set_exception_handler(async_json_exception_handler) + except Exception: + pass + + # Create a formatter and set it for the handler if json_logs: handler.setFormatter(JsonFormatter()) + _setup_json_exception_handlers(JsonFormatter()) else: formatter = logging.Formatter( "\033[92m%(asctime)s - %(name)s:%(levelname)s\033[0m: %(filename)s:%(lineno)s - %(message)s", @@ -65,18 +113,24 @@ def _turn_on_json(): handler = logging.StreamHandler() handler.setFormatter(JsonFormatter()) - # Define a list of the loggers to update - loggers = [verbose_router_logger, verbose_proxy_logger, verbose_logger] + # Define all loggers to update, including root logger + loggers = [logging.getLogger()] + [ + verbose_router_logger, + verbose_proxy_logger, + verbose_logger, + ] # Iterate through each logger and update its handlers for logger in loggers: # Remove all existing handlers for h in logger.handlers[:]: logger.removeHandler(h) - # Add the new handler logger.addHandler(handler) + # Set up exception handlers + _setup_json_exception_handlers(JsonFormatter()) + def _turn_on_debug(): verbose_logger.setLevel(level=logging.DEBUG) # set package log to debug diff --git a/litellm/_redis.py b/litellm/_redis.py index 5b2f85b1af..5c3e7776d6 100644 --- a/litellm/_redis.py +++ b/litellm/_redis.py @@ -202,6 +202,7 @@ def init_redis_cluster(redis_kwargs) -> redis.RedisCluster: def _init_redis_sentinel(redis_kwargs) -> redis.Redis: sentinel_nodes = redis_kwargs.get("sentinel_nodes") + sentinel_password = redis_kwargs.get("sentinel_password") service_name = redis_kwargs.get("service_name") if not sentinel_nodes or not service_name: @@ -212,7 +213,11 @@ def _init_redis_sentinel(redis_kwargs) -> redis.Redis: verbose_logger.debug("init_redis_sentinel: sentinel nodes are being initialized.") # Set up the Sentinel client - sentinel = redis.Sentinel(sentinel_nodes, socket_timeout=0.1) + sentinel = redis.Sentinel( + sentinel_nodes, + socket_timeout=0.1, + password=sentinel_password, + ) # Return the master instance for the given service diff --git a/litellm/_service_logger.py b/litellm/_service_logger.py index 0b4f22e210..8f835bea83 100644 --- a/litellm/_service_logger.py +++ b/litellm/_service_logger.py @@ -15,7 +15,7 @@ from .types.services import ServiceLoggerPayload, ServiceTypes if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] OTELClass = OpenTelemetry else: Span = Any diff --git a/litellm/batches/main.py b/litellm/batches/main.py index 1ddcafce4c..f4f74c72fb 100644 --- a/litellm/batches/main.py +++ b/litellm/batches/main.py @@ -153,7 +153,6 @@ def create_batch( ) api_base: Optional[str] = None if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there api_base = ( optional_params.api_base @@ -358,7 +357,6 @@ def retrieve_batch( _is_async = kwargs.pop("aretrieve_batch", False) is True api_base: Optional[str] = None if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there api_base = ( optional_params.api_base diff --git a/litellm/caching/base_cache.py b/litellm/caching/base_cache.py index 7109951d15..5140b390f7 100644 --- a/litellm/caching/base_cache.py +++ b/litellm/caching/base_cache.py @@ -9,12 +9,12 @@ Has 4 methods: """ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Union if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/caching/caching_handler.py b/litellm/caching/caching_handler.py index 09fabf1c12..14278de9cd 100644 --- a/litellm/caching/caching_handler.py +++ b/litellm/caching/caching_handler.py @@ -66,9 +66,7 @@ class CachingHandlerResponse(BaseModel): cached_result: Optional[Any] = None final_embedding_cached_response: Optional[EmbeddingResponse] = None - embedding_all_elements_cache_hit: bool = ( - False # this is set to True when all elements in the list have a cache hit in the embedding cache, if true return the final_embedding_cached_response no need to make an API call - ) + embedding_all_elements_cache_hit: bool = False # this is set to True when all elements in the list have a cache hit in the embedding cache, if true return the final_embedding_cached_response no need to make an API call class LLMCachingHandler: @@ -738,7 +736,6 @@ class LLMCachingHandler: if self._should_store_result_in_cache( original_function=self.original_function, kwargs=new_kwargs ): - litellm.cache.add_cache(result, **new_kwargs) return @@ -865,9 +862,9 @@ class LLMCachingHandler: } if litellm.cache is not None: - litellm_params["preset_cache_key"] = ( - litellm.cache._get_preset_cache_key_from_kwargs(**kwargs) - ) + litellm_params[ + "preset_cache_key" + ] = litellm.cache._get_preset_cache_key_from_kwargs(**kwargs) else: litellm_params["preset_cache_key"] = None diff --git a/litellm/caching/disk_cache.py b/litellm/caching/disk_cache.py index abf3203f50..413ac2932d 100644 --- a/litellm/caching/disk_cache.py +++ b/litellm/caching/disk_cache.py @@ -1,12 +1,12 @@ import json -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Union from .base_cache import BaseCache if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/caching/dual_cache.py b/litellm/caching/dual_cache.py index 5f598f7d70..8bef333758 100644 --- a/litellm/caching/dual_cache.py +++ b/litellm/caching/dual_cache.py @@ -12,7 +12,7 @@ import asyncio import time import traceback from concurrent.futures import ThreadPoolExecutor -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional, Union import litellm from litellm._logging import print_verbose, verbose_logger @@ -24,7 +24,7 @@ from .redis_cache import RedisCache if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/caching/llm_caching_handler.py b/litellm/caching/llm_caching_handler.py index 429634b7b1..3bf1f80d08 100644 --- a/litellm/caching/llm_caching_handler.py +++ b/litellm/caching/llm_caching_handler.py @@ -8,7 +8,6 @@ from .in_memory_cache import InMemoryCache class LLMClientCache(InMemoryCache): - def update_cache_key_with_event_loop(self, key): """ Add the event loop to the cache key, to prevent event loop closed errors. diff --git a/litellm/caching/redis_cache.py b/litellm/caching/redis_cache.py index 0571ac9f15..63cd4d0959 100644 --- a/litellm/caching/redis_cache.py +++ b/litellm/caching/redis_cache.py @@ -34,7 +34,7 @@ if TYPE_CHECKING: cluster_pipeline = ClusterPipeline async_redis_client = Redis async_redis_cluster_client = RedisCluster - Span = _Span + Span = Union[_Span, Any] else: pipeline = Any cluster_pipeline = Any @@ -57,7 +57,6 @@ class RedisCache(BaseCache): socket_timeout: Optional[float] = 5.0, # default 5 second timeout **kwargs, ): - from litellm._service_logger import ServiceLogging from .._redis import get_redis_client, get_redis_connection_pool @@ -1045,3 +1044,109 @@ class RedisCache(BaseCache): except Exception as e: verbose_logger.debug(f"Redis TTL Error: {e}") return None + + async def async_rpush( + self, + key: str, + values: List[Any], + parent_otel_span: Optional[Span] = None, + **kwargs, + ) -> int: + """ + Append one or multiple values to a list stored at key + + Args: + key: The Redis key of the list + values: One or more values to append to the list + parent_otel_span: Optional parent OpenTelemetry span + + Returns: + int: The length of the list after the push operation + """ + _redis_client: Any = self.init_async_client() + start_time = time.time() + try: + response = await _redis_client.rpush(key, *values) + ## LOGGING ## + end_time = time.time() + _duration = end_time - start_time + asyncio.create_task( + self.service_logger_obj.async_service_success_hook( + service=ServiceTypes.REDIS, + duration=_duration, + call_type="async_rpush", + ) + ) + return response + except Exception as e: + # NON blocking - notify users Redis is throwing an exception + ## LOGGING ## + end_time = time.time() + _duration = end_time - start_time + asyncio.create_task( + self.service_logger_obj.async_service_failure_hook( + service=ServiceTypes.REDIS, + duration=_duration, + error=e, + call_type="async_rpush", + ) + ) + verbose_logger.error( + f"LiteLLM Redis Cache RPUSH: - Got exception from REDIS : {str(e)}" + ) + raise e + + async def async_lpop( + self, + key: str, + count: Optional[int] = None, + parent_otel_span: Optional[Span] = None, + **kwargs, + ) -> Union[Any, List[Any]]: + _redis_client: Any = self.init_async_client() + start_time = time.time() + print_verbose(f"LPOP from Redis list: key: {key}, count: {count}") + try: + result = await _redis_client.lpop(key, count) + ## LOGGING ## + end_time = time.time() + _duration = end_time - start_time + asyncio.create_task( + self.service_logger_obj.async_service_success_hook( + service=ServiceTypes.REDIS, + duration=_duration, + call_type="async_lpop", + ) + ) + + # Handle result parsing if needed + if isinstance(result, bytes): + try: + return result.decode("utf-8") + except Exception: + return result + elif isinstance(result, list) and all( + isinstance(item, bytes) for item in result + ): + try: + return [item.decode("utf-8") for item in result] + except Exception: + return result + return result + except Exception as e: + # NON blocking - notify users Redis is throwing an exception + ## LOGGING ## + end_time = time.time() + _duration = end_time - start_time + asyncio.create_task( + self.service_logger_obj.async_service_failure_hook( + service=ServiceTypes.REDIS, + duration=_duration, + error=e, + call_type="async_lpop", + ) + ) + verbose_logger.error( + f"LiteLLM Redis Cache LPOP: - Got exception from REDIS : {str(e)}" + ) + raise e diff --git a/litellm/caching/redis_cluster_cache.py b/litellm/caching/redis_cluster_cache.py index 2e7d1de17f..21c3ab0366 100644 --- a/litellm/caching/redis_cluster_cache.py +++ b/litellm/caching/redis_cluster_cache.py @@ -5,7 +5,7 @@ Key differences: - RedisClient NEEDs to be re-used across requests, adds 3000ms latency if it's re-created """ -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional, Union from litellm.caching.redis_cache import RedisCache @@ -16,7 +16,7 @@ if TYPE_CHECKING: pipeline = Pipeline async_redis_client = Redis - Span = _Span + Span = Union[_Span, Any] else: pipeline = Any async_redis_client = Any diff --git a/litellm/caching/redis_semantic_cache.py b/litellm/caching/redis_semantic_cache.py index f46bb661ef..c76f27377d 100644 --- a/litellm/caching/redis_semantic_cache.py +++ b/litellm/caching/redis_semantic_cache.py @@ -13,23 +13,27 @@ import ast import asyncio import json import os -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Tuple, cast import litellm from litellm._logging import print_verbose -from litellm.litellm_core_utils.prompt_templates.common_utils import get_str_from_messages +from litellm.litellm_core_utils.prompt_templates.common_utils import ( + get_str_from_messages, +) +from litellm.types.utils import EmbeddingResponse + from .base_cache import BaseCache class RedisSemanticCache(BaseCache): """ - Redis-backed semantic cache for LLM responses. - - This cache uses vector similarity to find semantically similar prompts that have been + Redis-backed semantic cache for LLM responses. + + This cache uses vector similarity to find semantically similar prompts that have been previously sent to the LLM, allowing for cache hits even when prompts are not identical but carry similar meaning. """ - + DEFAULT_REDIS_INDEX_NAME: str = "litellm_semantic_cache_index" def __init__( @@ -57,7 +61,7 @@ class RedisSemanticCache(BaseCache): index_name: Name for the Redis index ttl: Default time-to-live for cache entries in seconds **kwargs: Additional arguments passed to the Redis client - + Raises: Exception: If similarity_threshold is not provided or required Redis connection information is missing @@ -69,14 +73,14 @@ class RedisSemanticCache(BaseCache): index_name = self.DEFAULT_REDIS_INDEX_NAME print_verbose(f"Redis semantic-cache initializing index - {index_name}") - + # Validate similarity threshold if similarity_threshold is None: raise ValueError("similarity_threshold must be provided, passed None") - + # Store configuration self.similarity_threshold = similarity_threshold - + # Convert similarity threshold [0,1] to distance threshold [0,2] # For cosine distance: 0 = most similar, 2 = least similar # While similarity: 1 = most similar, 0 = least similar @@ -87,14 +91,16 @@ class RedisSemanticCache(BaseCache): if redis_url is None: try: # Attempt to use provided parameters or fallback to environment variables - host = host or os.environ['REDIS_HOST'] - port = port or os.environ['REDIS_PORT'] - password = password or os.environ['REDIS_PASSWORD'] + host = host or os.environ["REDIS_HOST"] + port = port or os.environ["REDIS_PORT"] + password = password or os.environ["REDIS_PASSWORD"] except KeyError as e: # Raise a more informative exception if any of the required keys are missing missing_var = e.args[0] - raise ValueError(f"Missing required Redis configuration: {missing_var}. " - f"Provide {missing_var} or redis_url.") from e + raise ValueError( + f"Missing required Redis configuration: {missing_var}. " + f"Provide {missing_var} or redis_url." + ) from e redis_url = f"redis://:{password}@{host}:{port}" @@ -114,7 +120,7 @@ class RedisSemanticCache(BaseCache): def _get_ttl(self, **kwargs) -> Optional[int]: """ Get the TTL (time-to-live) value for cache entries. - + Args: **kwargs: Keyword arguments that may contain a custom TTL @@ -125,22 +131,25 @@ class RedisSemanticCache(BaseCache): if ttl is not None: ttl = int(ttl) return ttl - + def _get_embedding(self, prompt: str) -> List[float]: """ Generate an embedding vector for the given prompt using the configured embedding model. - + Args: prompt: The text to generate an embedding for - + Returns: List[float]: The embedding vector """ # Create an embedding from prompt - embedding_response = litellm.embedding( - model=self.embedding_model, - input=prompt, - cache={"no-store": True, "no-cache": True}, + embedding_response = cast( + EmbeddingResponse, + litellm.embedding( + model=self.embedding_model, + input=prompt, + cache={"no-store": True, "no-cache": True}, + ), ) embedding = embedding_response["data"][0]["embedding"] return embedding @@ -148,10 +157,10 @@ class RedisSemanticCache(BaseCache): def _get_cache_logic(self, cached_response: Any) -> Any: """ Process the cached response to prepare it for use. - + Args: cached_response: The raw cached response - + Returns: The processed cache response, or None if input was None """ @@ -171,13 +180,13 @@ class RedisSemanticCache(BaseCache): except (ValueError, SyntaxError) as e: print_verbose(f"Error parsing cached response: {str(e)}") return None - + return cached_response def set_cache(self, key: str, value: Any, **kwargs) -> None: """ Store a value in the semantic cache. - + Args: key: The cache key (not directly used in semantic caching) value: The response value to cache @@ -186,13 +195,14 @@ class RedisSemanticCache(BaseCache): """ print_verbose(f"Redis semantic-cache set_cache, kwargs: {kwargs}") + value_str: Optional[str] = None try: # Extract the prompt from messages messages = kwargs.get("messages", []) if not messages: print_verbose("No messages provided for semantic caching") return - + prompt = get_str_from_messages(messages) value_str = str(value) @@ -203,16 +213,18 @@ class RedisSemanticCache(BaseCache): else: self.llmcache.store(prompt, value_str) except Exception as e: - print_verbose(f"Error setting {value_str} in the Redis semantic cache: {str(e)}") + print_verbose( + f"Error setting {value_str or value} in the Redis semantic cache: {str(e)}" + ) def get_cache(self, key: str, **kwargs) -> Any: """ Retrieve a semantically similar cached response. - + Args: key: The cache key (not directly used in semantic caching) **kwargs: Additional arguments including 'messages' for the prompt - + Returns: The cached response if a semantically similar prompt is found, else None """ @@ -224,7 +236,7 @@ class RedisSemanticCache(BaseCache): if not messages: print_verbose("No messages provided for semantic cache lookup") return None - + prompt = get_str_from_messages(messages) # Check the cache for semantically similar prompts results = self.llmcache.check(prompt=prompt) @@ -236,12 +248,12 @@ class RedisSemanticCache(BaseCache): # Process the best matching result cache_hit = results[0] vector_distance = float(cache_hit["vector_distance"]) - + # Convert vector distance back to similarity score # For cosine distance: 0 = most similar, 2 = least similar # While similarity: 1 = most similar, 0 = least similar similarity = 1 - vector_distance - + cached_prompt = cache_hit["prompt"] cached_response = cache_hit["response"] @@ -251,19 +263,19 @@ class RedisSemanticCache(BaseCache): f"current prompt: {prompt}, " f"cached prompt: {cached_prompt}" ) - + return self._get_cache_logic(cached_response=cached_response) except Exception as e: print_verbose(f"Error retrieving from Redis semantic cache: {str(e)}") - + async def _get_async_embedding(self, prompt: str, **kwargs) -> List[float]: """ Asynchronously generate an embedding for the given prompt. - + Args: prompt: The text to generate an embedding for **kwargs: Additional arguments that may contain metadata - + Returns: List[float]: The embedding vector """ @@ -275,7 +287,7 @@ class RedisSemanticCache(BaseCache): if llm_model_list is not None else [] ) - + try: if llm_router is not None and self.embedding_model in router_model_names: # Use the router for embedding generation @@ -307,7 +319,7 @@ class RedisSemanticCache(BaseCache): async def async_set_cache(self, key: str, value: Any, **kwargs) -> None: """ Asynchronously store a value in the semantic cache. - + Args: key: The cache key (not directly used in semantic caching) value: The response value to cache @@ -322,13 +334,13 @@ class RedisSemanticCache(BaseCache): if not messages: print_verbose("No messages provided for semantic caching") return - + prompt = get_str_from_messages(messages) value_str = str(value) # Generate embedding for the value (response) to cache prompt_embedding = await self._get_async_embedding(prompt, **kwargs) - + # Get TTL and store in Redis semantic cache ttl = self._get_ttl(**kwargs) if ttl is not None: @@ -336,13 +348,13 @@ class RedisSemanticCache(BaseCache): prompt, value_str, vector=prompt_embedding, # Pass through custom embedding - ttl=ttl + ttl=ttl, ) else: await self.llmcache.astore( prompt, value_str, - vector=prompt_embedding # Pass through custom embedding + vector=prompt_embedding, # Pass through custom embedding ) except Exception as e: print_verbose(f"Error in async_set_cache: {str(e)}") @@ -350,11 +362,11 @@ class RedisSemanticCache(BaseCache): async def async_get_cache(self, key: str, **kwargs) -> Any: """ Asynchronously retrieve a semantically similar cached response. - + Args: key: The cache key (not directly used in semantic caching) **kwargs: Additional arguments including 'messages' for the prompt - + Returns: The cached response if a semantically similar prompt is found, else None """ @@ -367,21 +379,20 @@ class RedisSemanticCache(BaseCache): print_verbose("No messages provided for semantic cache lookup") kwargs.setdefault("metadata", {})["semantic-similarity"] = 0.0 return None - + prompt = get_str_from_messages(messages) - + # Generate embedding for the prompt prompt_embedding = await self._get_async_embedding(prompt, **kwargs) # Check the cache for semantically similar prompts - results = await self.llmcache.acheck( - prompt=prompt, - vector=prompt_embedding - ) + results = await self.llmcache.acheck(prompt=prompt, vector=prompt_embedding) # handle results / cache hit if not results: - kwargs.setdefault("metadata", {})["semantic-similarity"] = 0.0 # TODO why here but not above?? + kwargs.setdefault("metadata", {})[ + "semantic-similarity" + ] = 0.0 # TODO why here but not above?? return None cache_hit = results[0] @@ -404,7 +415,7 @@ class RedisSemanticCache(BaseCache): f"current prompt: {prompt}, " f"cached prompt: {cached_prompt}" ) - + return self._get_cache_logic(cached_response=cached_response) except Exception as e: print_verbose(f"Error in async_get_cache: {str(e)}") @@ -413,17 +424,19 @@ class RedisSemanticCache(BaseCache): async def _index_info(self) -> Dict[str, Any]: """ Get information about the Redis index. - + Returns: Dict[str, Any]: Information about the Redis index """ aindex = await self.llmcache._get_async_index() return await aindex.info() - async def async_set_cache_pipeline(self, cache_list: List[Tuple[str, Any]], **kwargs) -> None: + async def async_set_cache_pipeline( + self, cache_list: List[Tuple[str, Any]], **kwargs + ) -> None: """ Asynchronously store multiple values in the semantic cache. - + Args: cache_list: List of (key, value) tuples to cache **kwargs: Additional arguments diff --git a/litellm/caching/s3_cache.py b/litellm/caching/s3_cache.py index 301591c64f..c02e109136 100644 --- a/litellm/caching/s3_cache.py +++ b/litellm/caching/s3_cache.py @@ -123,7 +123,7 @@ class S3Cache(BaseCache): ) # Convert string to dictionary except Exception: cached_response = ast.literal_eval(cached_response) - if type(cached_response) is not dict: + if not isinstance(cached_response, dict): cached_response = dict(cached_response) verbose_logger.debug( f"Got S3 Cache: key: {key}, cached_response {cached_response}. Type Response {type(cached_response)}" diff --git a/litellm/constants.py b/litellm/constants.py index da66f897c9..d5e0215ebf 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -4,9 +4,11 @@ ROUTER_MAX_FALLBACKS = 5 DEFAULT_BATCH_SIZE = 512 DEFAULT_FLUSH_INTERVAL_SECONDS = 5 DEFAULT_MAX_RETRIES = 2 +DEFAULT_MAX_RECURSE_DEPTH = 10 DEFAULT_FAILURE_THRESHOLD_PERCENT = ( 0.5 # default cooldown a deployment if 50% of requests fail in a given minute ) +DEFAULT_MAX_TOKENS = 4096 DEFAULT_REDIS_SYNC_INTERVAL = 1 DEFAULT_COOLDOWN_TIME_SECONDS = 5 DEFAULT_REPLICATE_POLLING_RETRIES = 5 @@ -16,6 +18,8 @@ DEFAULT_IMAGE_WIDTH = 300 DEFAULT_IMAGE_HEIGHT = 300 MAX_SIZE_PER_ITEM_IN_MEMORY_CACHE_IN_KB = 1024 # 1MB = 1024KB SINGLE_DEPLOYMENT_TRAFFIC_FAILURE_THRESHOLD = 1000 # Minimum number of requests to consider "reasonable traffic". Used for single-deployment cooldown logic. +REDIS_UPDATE_BUFFER_KEY = "litellm_spend_update_buffer" +MAX_REDIS_BUFFER_DEQUEUE_COUNT = 100 #### RELIABILITY #### REPEATED_STREAMING_CHUNK_LIMIT = 100 # catch if model starts looping the same chunk while streaming. Uses high default to prevent false positives. #### Networking settings #### @@ -414,6 +418,7 @@ RESPONSE_FORMAT_TOOL_NAME = "json_tool_call" # default tool name used when conv ########################### Logging Callback Constants ########################### AZURE_STORAGE_MSFT_VERSION = "2019-07-07" +MCP_TOOL_NAME_PREFIX = "mcp_tool" ########################### LiteLLM Proxy Specific Constants ########################### ######################################################################################## @@ -441,3 +446,7 @@ HEALTH_CHECK_TIMEOUT_SECONDS = 60 # 60 seconds UI_SESSION_TOKEN_TEAM_ID = "litellm-dashboard" LITELLM_PROXY_ADMIN_NAME = "default_user_id" + +########################### DB CRON JOB NAMES ########################### +DB_SPEND_UPDATE_JOB_NAME = "db_spend_update_job" +DEFAULT_CRON_JOB_LOCK_TTL_SECONDS = 60 # 1 minute diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index f5731618a3..a41fc364ab 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -2,7 +2,7 @@ ## File for 'response_cost' calculation in Logging import time from functools import lru_cache -from typing import Any, List, Literal, Optional, Tuple, Union +from typing import Any, List, Literal, Optional, Tuple, Union, cast from pydantic import BaseModel @@ -275,15 +275,13 @@ def cost_per_token( # noqa: PLR0915 custom_llm_provider=custom_llm_provider, prompt_characters=prompt_characters, completion_characters=completion_characters, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage_block, ) elif cost_router == "cost_per_token": return google_cost_per_token( model=model_without_prefix, custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage_block, ) elif custom_llm_provider == "anthropic": return anthropic_cost_per_token(model=model, usage=usage_block) @@ -464,13 +462,36 @@ def _model_contains_known_llm_provider(model: str) -> bool: def _get_usage_object( completion_response: Any, ) -> Optional[Usage]: - usage_obj: Optional[Usage] = None - if completion_response is not None and isinstance( - completion_response, ModelResponse - ): - usage_obj = completion_response.get("usage") + usage_obj = cast( + Union[Usage, ResponseAPIUsage, dict, BaseModel], + ( + completion_response.get("usage") + if isinstance(completion_response, dict) + else getattr(completion_response, "get", lambda x: None)("usage") + ), + ) - return usage_obj + if usage_obj is None: + return None + if isinstance(usage_obj, Usage): + return usage_obj + elif ( + usage_obj is not None + and (isinstance(usage_obj, dict) or isinstance(usage_obj, ResponseAPIUsage)) + and ResponseAPILoggingUtils._is_response_api_usage(usage_obj) + ): + return ResponseAPILoggingUtils._transform_response_api_usage_to_chat_usage( + usage_obj + ) + elif isinstance(usage_obj, dict): + return Usage(**usage_obj) + elif isinstance(usage_obj, BaseModel): + return Usage(**usage_obj.model_dump()) + else: + verbose_logger.debug( + f"Unknown usage object type: {type(usage_obj)}, usage_obj: {usage_obj}" + ) + return None def _is_known_usage_objects(usage_obj): @@ -559,7 +580,6 @@ def completion_cost( # noqa: PLR0915 - For un-mapped Replicate models, the cost is calculated based on the total time used for the request. """ try: - call_type = _infer_call_type(call_type, completion_response) or "completion" if ( @@ -664,6 +684,7 @@ def completion_cost( # noqa: PLR0915 elif len(prompt) > 0: prompt_tokens = token_counter(model=model, text=prompt) completion_tokens = token_counter(model=model, text=completion) + if model is None: raise ValueError( f"Model is None and does not exist in passed completion_response. Passed completion_response={completion_response}, model={model}" diff --git a/litellm/experimental_mcp_client/tools.py b/litellm/experimental_mcp_client/tools.py index f4ebbf4af4..cdc26af4b7 100644 --- a/litellm/experimental_mcp_client/tools.py +++ b/litellm/experimental_mcp_client/tools.py @@ -1,5 +1,5 @@ import json -from typing import List, Literal, Union +from typing import Dict, List, Literal, Union from mcp import ClientSession from mcp.types import CallToolRequestParams as MCPCallToolRequestParams @@ -76,8 +76,8 @@ def _get_function_arguments(function: FunctionDefinition) -> dict: return arguments if isinstance(arguments, dict) else {} -def _transform_openai_tool_call_to_mcp_tool_call_request( - openai_tool: ChatCompletionMessageToolCall, +def transform_openai_tool_call_request_to_mcp_tool_call_request( + openai_tool: Union[ChatCompletionMessageToolCall, Dict], ) -> MCPCallToolRequestParams: """Convert an OpenAI ChatCompletionMessageToolCall to an MCP CallToolRequestParams.""" function = openai_tool["function"] @@ -100,8 +100,10 @@ async def call_openai_tool( Returns: The result of the MCP tool call. """ - mcp_tool_call_request_params = _transform_openai_tool_call_to_mcp_tool_call_request( - openai_tool=openai_tool, + mcp_tool_call_request_params = ( + transform_openai_tool_call_request_to_mcp_tool_call_request( + openai_tool=openai_tool, + ) ) return await call_mcp_tool( session=session, diff --git a/litellm/fine_tuning/main.py b/litellm/fine_tuning/main.py index b726a394c2..09c070fffb 100644 --- a/litellm/fine_tuning/main.py +++ b/litellm/fine_tuning/main.py @@ -138,7 +138,6 @@ def create_fine_tuning_job( # OpenAI if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there api_base = ( optional_params.api_base @@ -360,7 +359,6 @@ def cancel_fine_tuning_job( # OpenAI if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there api_base = ( optional_params.api_base @@ -522,7 +520,6 @@ def list_fine_tuning_jobs( # OpenAI if custom_llm_provider == "openai": - # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there api_base = ( optional_params.api_base diff --git a/litellm/integrations/SlackAlerting/batching_handler.py b/litellm/integrations/SlackAlerting/batching_handler.py index e35cf61d63..fdce2e0479 100644 --- a/litellm/integrations/SlackAlerting/batching_handler.py +++ b/litellm/integrations/SlackAlerting/batching_handler.py @@ -19,7 +19,6 @@ else: def squash_payloads(queue): - squashed = {} if len(queue) == 0: return squashed diff --git a/litellm/integrations/SlackAlerting/slack_alerting.py b/litellm/integrations/SlackAlerting/slack_alerting.py index a2e6264760..50f0538cfd 100644 --- a/litellm/integrations/SlackAlerting/slack_alerting.py +++ b/litellm/integrations/SlackAlerting/slack_alerting.py @@ -195,12 +195,15 @@ class SlackAlerting(CustomBatchLogger): if self.alerting is None or self.alert_types is None: return - time_difference_float, model, api_base, messages = ( - self._response_taking_too_long_callback_helper( - kwargs=kwargs, - start_time=start_time, - end_time=end_time, - ) + ( + time_difference_float, + model, + api_base, + messages, + ) = self._response_taking_too_long_callback_helper( + kwargs=kwargs, + start_time=start_time, + end_time=end_time, ) if litellm.turn_off_message_logging or litellm.redact_messages_in_exceptions: messages = "Message not logged. litellm.redact_messages_in_exceptions=True" @@ -819,9 +822,9 @@ class SlackAlerting(CustomBatchLogger): ### UNIQUE CACHE KEY ### cache_key = provider + region_name - outage_value: Optional[ProviderRegionOutageModel] = ( - await self.internal_usage_cache.async_get_cache(key=cache_key) - ) + outage_value: Optional[ + ProviderRegionOutageModel + ] = await self.internal_usage_cache.async_get_cache(key=cache_key) if ( getattr(exception, "status_code", None) is None @@ -1402,9 +1405,9 @@ Model Info: self.alert_to_webhook_url is not None and alert_type in self.alert_to_webhook_url ): - slack_webhook_url: Optional[Union[str, List[str]]] = ( - self.alert_to_webhook_url[alert_type] - ) + slack_webhook_url: Optional[ + Union[str, List[str]] + ] = self.alert_to_webhook_url[alert_type] elif self.default_webhook_url is not None: slack_webhook_url = self.default_webhook_url else: @@ -1768,7 +1771,6 @@ Model Info: - Team Created, Updated, Deleted """ try: - message = f"`{event_name}`\n" key_event_dict = key_event.model_dump() diff --git a/litellm/integrations/_types/open_inference.py b/litellm/integrations/_types/open_inference.py index b5076c0e42..bcfabe9b7b 100644 --- a/litellm/integrations/_types/open_inference.py +++ b/litellm/integrations/_types/open_inference.py @@ -283,4 +283,4 @@ class OpenInferenceSpanKindValues(Enum): class OpenInferenceMimeTypeValues(Enum): TEXT = "text/plain" - JSON = "application/json" \ No newline at end of file + JSON = "application/json" diff --git a/litellm/integrations/argilla.py b/litellm/integrations/argilla.py index 055ad90259..a362ce7e4d 100644 --- a/litellm/integrations/argilla.py +++ b/litellm/integrations/argilla.py @@ -98,7 +98,6 @@ class ArgillaLogger(CustomBatchLogger): argilla_dataset_name: Optional[str], argilla_base_url: Optional[str], ) -> ArgillaCredentialsObject: - _credentials_api_key = argilla_api_key or os.getenv("ARGILLA_API_KEY") if _credentials_api_key is None: raise Exception("Invalid Argilla API Key given. _credentials_api_key=None.") diff --git a/litellm/integrations/arize/_utils.py b/litellm/integrations/arize/_utils.py index 487304cce4..5a090968b4 100644 --- a/litellm/integrations/arize/_utils.py +++ b/litellm/integrations/arize/_utils.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Union from litellm._logging import verbose_logger from litellm.litellm_core_utils.safe_json_dumps import safe_dumps @@ -7,7 +7,7 @@ from litellm.types.utils import StandardLoggingPayload if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/integrations/arize/arize.py b/litellm/integrations/arize/arize.py index 7a0fb785a7..03b6966809 100644 --- a/litellm/integrations/arize/arize.py +++ b/litellm/integrations/arize/arize.py @@ -19,14 +19,13 @@ if TYPE_CHECKING: from litellm.types.integrations.arize import Protocol as _Protocol Protocol = _Protocol - Span = _Span + Span = Union[_Span, Any] else: Protocol = Any Span = Any class ArizeLogger(OpenTelemetry): - def set_attributes(self, span: Span, kwargs, response_obj: Optional[Any]): ArizeLogger.set_arize_attributes(span, kwargs, response_obj) return diff --git a/litellm/integrations/arize/arize_phoenix.py b/litellm/integrations/arize/arize_phoenix.py index d7b7d5812b..2b4909885a 100644 --- a/litellm/integrations/arize/arize_phoenix.py +++ b/litellm/integrations/arize/arize_phoenix.py @@ -1,17 +1,20 @@ import os -from typing import TYPE_CHECKING, Any -from litellm.integrations.arize import _utils +from typing import TYPE_CHECKING, Any, Union + from litellm._logging import verbose_logger +from litellm.integrations.arize import _utils from litellm.types.integrations.arize_phoenix import ArizePhoenixConfig if TYPE_CHECKING: - from .opentelemetry import OpenTelemetryConfig as _OpenTelemetryConfig - from litellm.types.integrations.arize import Protocol as _Protocol from opentelemetry.trace import Span as _Span + from litellm.types.integrations.arize import Protocol as _Protocol + + from .opentelemetry import OpenTelemetryConfig as _OpenTelemetryConfig + Protocol = _Protocol OpenTelemetryConfig = _OpenTelemetryConfig - Span = _Span + Span = Union[_Span, Any] else: Protocol = Any OpenTelemetryConfig = Any @@ -20,6 +23,7 @@ else: ARIZE_HOSTED_PHOENIX_ENDPOINT = "https://app.phoenix.arize.com/v1/traces" + class ArizePhoenixLogger: @staticmethod def set_arize_phoenix_attributes(span: Span, kwargs, response_obj): @@ -49,7 +53,7 @@ class ArizePhoenixLogger: protocol = "otlp_grpc" else: endpoint = ARIZE_HOSTED_PHOENIX_ENDPOINT - protocol = "otlp_http" + protocol = "otlp_http" verbose_logger.debug( f"No PHOENIX_COLLECTOR_ENDPOINT or PHOENIX_COLLECTOR_HTTP_ENDPOINT found, using default endpoint with http: {ARIZE_HOSTED_PHOENIX_ENDPOINT}" ) @@ -57,17 +61,16 @@ class ArizePhoenixLogger: otlp_auth_headers = None # If the endpoint is the Arize hosted Phoenix endpoint, use the api_key as the auth header as currently it is uses # a slightly different auth header format than self hosted phoenix - if endpoint == ARIZE_HOSTED_PHOENIX_ENDPOINT: + if endpoint == ARIZE_HOSTED_PHOENIX_ENDPOINT: if api_key is None: - raise ValueError("PHOENIX_API_KEY must be set when the Arize hosted Phoenix endpoint is used.") + raise ValueError( + "PHOENIX_API_KEY must be set when the Arize hosted Phoenix endpoint is used." + ) otlp_auth_headers = f"api_key={api_key}" elif api_key is not None: # api_key/auth is optional for self hosted phoenix otlp_auth_headers = f"Authorization=Bearer {api_key}" return ArizePhoenixConfig( - otlp_auth_headers=otlp_auth_headers, - protocol=protocol, - endpoint=endpoint + otlp_auth_headers=otlp_auth_headers, protocol=protocol, endpoint=endpoint ) - diff --git a/litellm/integrations/athina.py b/litellm/integrations/athina.py index 705dc11f1d..49b9e9e687 100644 --- a/litellm/integrations/athina.py +++ b/litellm/integrations/athina.py @@ -12,7 +12,10 @@ class AthinaLogger: "athina-api-key": self.athina_api_key, "Content-Type": "application/json", } - self.athina_logging_url = os.getenv("ATHINA_BASE_URL", "https://log.athina.ai") + "/api/v1/log/inference" + self.athina_logging_url = ( + os.getenv("ATHINA_BASE_URL", "https://log.athina.ai") + + "/api/v1/log/inference" + ) self.additional_keys = [ "environment", "prompt_slug", diff --git a/litellm/integrations/azure_storage/azure_storage.py b/litellm/integrations/azure_storage/azure_storage.py index ddc46b117f..27f5e0e112 100644 --- a/litellm/integrations/azure_storage/azure_storage.py +++ b/litellm/integrations/azure_storage/azure_storage.py @@ -50,12 +50,12 @@ class AzureBlobStorageLogger(CustomBatchLogger): self.azure_storage_file_system: str = _azure_storage_file_system # Internal variables used for Token based authentication - self.azure_auth_token: Optional[str] = ( - None # the Azure AD token to use for Azure Storage API requests - ) - self.token_expiry: Optional[datetime] = ( - None # the expiry time of the currentAzure AD token - ) + self.azure_auth_token: Optional[ + str + ] = None # the Azure AD token to use for Azure Storage API requests + self.token_expiry: Optional[ + datetime + ] = None # the expiry time of the currentAzure AD token asyncio.create_task(self.periodic_flush()) self.flush_lock = asyncio.Lock() @@ -153,7 +153,6 @@ class AzureBlobStorageLogger(CustomBatchLogger): 3. Flush the data """ try: - if self.azure_storage_account_key: await self.upload_to_azure_data_lake_with_azure_account_key( payload=payload diff --git a/litellm/integrations/braintrust_logging.py b/litellm/integrations/braintrust_logging.py index 281fbda01e..0961eab02b 100644 --- a/litellm/integrations/braintrust_logging.py +++ b/litellm/integrations/braintrust_logging.py @@ -4,7 +4,7 @@ import copy import os from datetime import datetime -from typing import Optional, Dict +from typing import Dict, Optional import httpx from pydantic import BaseModel @@ -19,7 +19,9 @@ from litellm.llms.custom_httpx.http_handler import ( ) from litellm.utils import print_verbose -global_braintrust_http_handler = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback) +global_braintrust_http_handler = get_async_httpx_client( + llm_provider=httpxSpecialProvider.LoggingCallback +) global_braintrust_sync_http_handler = HTTPHandler() API_BASE = "https://api.braintrustdata.com/v1" @@ -35,7 +37,9 @@ def get_utc_datetime(): class BraintrustLogger(CustomLogger): - def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None) -> None: + def __init__( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> None: super().__init__() self.validate_environment(api_key=api_key) self.api_base = api_base or API_BASE @@ -45,7 +49,9 @@ class BraintrustLogger(CustomLogger): "Authorization": "Bearer " + self.api_key, "Content-Type": "application/json", } - self._project_id_cache: Dict[str, str] = {} # Cache mapping project names to IDs + self._project_id_cache: Dict[ + str, str + ] = {} # Cache mapping project names to IDs def validate_environment(self, api_key: Optional[str]): """ @@ -71,7 +77,9 @@ class BraintrustLogger(CustomLogger): try: response = global_braintrust_sync_http_handler.post( - f"{self.api_base}/project", headers=self.headers, json={"name": project_name} + f"{self.api_base}/project", + headers=self.headers, + json={"name": project_name}, ) project_dict = response.json() project_id = project_dict["id"] @@ -89,7 +97,9 @@ class BraintrustLogger(CustomLogger): try: response = await global_braintrust_http_handler.post( - f"{self.api_base}/project/register", headers=self.headers, json={"name": project_name} + f"{self.api_base}/project/register", + headers=self.headers, + json={"name": project_name}, ) project_dict = response.json() project_id = project_dict["id"] @@ -116,15 +126,21 @@ class BraintrustLogger(CustomLogger): if metadata is None: metadata = {} - proxy_headers = litellm_params.get("proxy_server_request", {}).get("headers", {}) or {} + proxy_headers = ( + litellm_params.get("proxy_server_request", {}).get("headers", {}) or {} + ) for metadata_param_key in proxy_headers: if metadata_param_key.startswith("braintrust"): trace_param_key = metadata_param_key.replace("braintrust", "", 1) if trace_param_key in metadata: - verbose_logger.warning(f"Overwriting Braintrust `{trace_param_key}` from request header") + verbose_logger.warning( + f"Overwriting Braintrust `{trace_param_key}` from request header" + ) else: - verbose_logger.debug(f"Found Braintrust `{trace_param_key}` in request header") + verbose_logger.debug( + f"Found Braintrust `{trace_param_key}` in request header" + ) metadata[trace_param_key] = proxy_headers.get(metadata_param_key) return metadata @@ -157,24 +173,35 @@ class BraintrustLogger(CustomLogger): output = None choices = [] if response_obj is not None and ( - kwargs.get("call_type", None) == "embedding" or isinstance(response_obj, litellm.EmbeddingResponse) + kwargs.get("call_type", None) == "embedding" + or isinstance(response_obj, litellm.EmbeddingResponse) ): output = None - elif response_obj is not None and isinstance(response_obj, litellm.ModelResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.ModelResponse + ): output = response_obj["choices"][0]["message"].json() choices = response_obj["choices"] - elif response_obj is not None and isinstance(response_obj, litellm.TextCompletionResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.TextCompletionResponse + ): output = response_obj.choices[0].text choices = response_obj.choices - elif response_obj is not None and isinstance(response_obj, litellm.ImageResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.ImageResponse + ): output = response_obj["data"] litellm_params = kwargs.get("litellm_params", {}) - metadata = litellm_params.get("metadata", {}) or {} # if litellm_params['metadata'] == None + metadata = ( + litellm_params.get("metadata", {}) or {} + ) # if litellm_params['metadata'] == None metadata = self.add_metadata_from_header(litellm_params, metadata) clean_metadata = {} try: - metadata = copy.deepcopy(metadata) # Avoid modifying the original metadata + metadata = copy.deepcopy( + metadata + ) # Avoid modifying the original metadata except Exception: new_metadata = {} for key, value in metadata.items(): @@ -192,7 +219,9 @@ class BraintrustLogger(CustomLogger): project_id = metadata.get("project_id") if project_id is None: project_name = metadata.get("project_name") - project_id = self.get_project_id_sync(project_name) if project_name else None + project_id = ( + self.get_project_id_sync(project_name) if project_name else None + ) if project_id is None: if self.default_project_id is None: @@ -234,7 +263,8 @@ class BraintrustLogger(CustomLogger): "completion_tokens": usage_obj.completion_tokens, "total_tokens": usage_obj.total_tokens, "total_cost": cost, - "time_to_first_token": end_time.timestamp() - start_time.timestamp(), + "time_to_first_token": end_time.timestamp() + - start_time.timestamp(), "start": start_time.timestamp(), "end": end_time.timestamp(), } @@ -255,7 +285,9 @@ class BraintrustLogger(CustomLogger): request_data["metrics"] = metrics try: - print_verbose(f"global_braintrust_sync_http_handler.post: {global_braintrust_sync_http_handler.post}") + print_verbose( + f"global_braintrust_sync_http_handler.post: {global_braintrust_sync_http_handler.post}" + ) global_braintrust_sync_http_handler.post( url=f"{self.api_base}/project_logs/{project_id}/insert", json={"events": [request_data]}, @@ -276,20 +308,29 @@ class BraintrustLogger(CustomLogger): output = None choices = [] if response_obj is not None and ( - kwargs.get("call_type", None) == "embedding" or isinstance(response_obj, litellm.EmbeddingResponse) + kwargs.get("call_type", None) == "embedding" + or isinstance(response_obj, litellm.EmbeddingResponse) ): output = None - elif response_obj is not None and isinstance(response_obj, litellm.ModelResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.ModelResponse + ): output = response_obj["choices"][0]["message"].json() choices = response_obj["choices"] - elif response_obj is not None and isinstance(response_obj, litellm.TextCompletionResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.TextCompletionResponse + ): output = response_obj.choices[0].text choices = response_obj.choices - elif response_obj is not None and isinstance(response_obj, litellm.ImageResponse): + elif response_obj is not None and isinstance( + response_obj, litellm.ImageResponse + ): output = response_obj["data"] litellm_params = kwargs.get("litellm_params", {}) - metadata = litellm_params.get("metadata", {}) or {} # if litellm_params['metadata'] == None + metadata = ( + litellm_params.get("metadata", {}) or {} + ) # if litellm_params['metadata'] == None metadata = self.add_metadata_from_header(litellm_params, metadata) clean_metadata = {} new_metadata = {} @@ -313,7 +354,11 @@ class BraintrustLogger(CustomLogger): project_id = metadata.get("project_id") if project_id is None: project_name = metadata.get("project_name") - project_id = await self.get_project_id_async(project_name) if project_name else None + project_id = ( + await self.get_project_id_async(project_name) + if project_name + else None + ) if project_id is None: if self.default_project_id is None: @@ -362,8 +407,14 @@ class BraintrustLogger(CustomLogger): api_call_start_time = kwargs.get("api_call_start_time") completion_start_time = kwargs.get("completion_start_time") - if api_call_start_time is not None and completion_start_time is not None: - metrics["time_to_first_token"] = completion_start_time.timestamp() - api_call_start_time.timestamp() + if ( + api_call_start_time is not None + and completion_start_time is not None + ): + metrics["time_to_first_token"] = ( + completion_start_time.timestamp() + - api_call_start_time.timestamp() + ) request_data = { "id": litellm_call_id, diff --git a/litellm/integrations/custom_batch_logger.py b/litellm/integrations/custom_batch_logger.py index 3cfdf82cab..f9d4496c21 100644 --- a/litellm/integrations/custom_batch_logger.py +++ b/litellm/integrations/custom_batch_logger.py @@ -14,7 +14,6 @@ from litellm.integrations.custom_logger import CustomLogger class CustomBatchLogger(CustomLogger): - def __init__( self, flush_lock: Optional[asyncio.Lock] = None, diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py index 4421664bfc..41a3800116 100644 --- a/litellm/integrations/custom_guardrail.py +++ b/litellm/integrations/custom_guardrail.py @@ -7,7 +7,6 @@ from litellm.types.utils import StandardLoggingGuardrailInformation class CustomGuardrail(CustomLogger): - def __init__( self, guardrail_name: Optional[str] = None, diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py index 6f1ec88d01..ddb8094285 100644 --- a/litellm/integrations/custom_logger.py +++ b/litellm/integrations/custom_logger.py @@ -31,7 +31,7 @@ from litellm.types.utils import ( if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/integrations/datadog/datadog.py b/litellm/integrations/datadog/datadog.py index 4f4b05c84e..e9b6b6b164 100644 --- a/litellm/integrations/datadog/datadog.py +++ b/litellm/integrations/datadog/datadog.py @@ -233,7 +233,6 @@ class DataDogLogger( pass async def _log_async_event(self, kwargs, response_obj, start_time, end_time): - dd_payload = self.create_datadog_logging_payload( kwargs=kwargs, response_obj=response_obj, diff --git a/litellm/integrations/gcs_bucket/gcs_bucket_base.py b/litellm/integrations/gcs_bucket/gcs_bucket_base.py index 66995d8482..0ce845ecb2 100644 --- a/litellm/integrations/gcs_bucket/gcs_bucket_base.py +++ b/litellm/integrations/gcs_bucket/gcs_bucket_base.py @@ -125,9 +125,9 @@ class GCSBucketBase(CustomBatchLogger): if kwargs is None: kwargs = {} - standard_callback_dynamic_params: Optional[StandardCallbackDynamicParams] = ( - kwargs.get("standard_callback_dynamic_params", None) - ) + standard_callback_dynamic_params: Optional[ + StandardCallbackDynamicParams + ] = kwargs.get("standard_callback_dynamic_params", None) bucket_name: str path_service_account: Optional[str] diff --git a/litellm/integrations/gcs_pubsub/pub_sub.py b/litellm/integrations/gcs_pubsub/pub_sub.py index 1b078df7bc..bdaedcd908 100644 --- a/litellm/integrations/gcs_pubsub/pub_sub.py +++ b/litellm/integrations/gcs_pubsub/pub_sub.py @@ -70,12 +70,13 @@ class GcsPubSubLogger(CustomBatchLogger): """Construct authorization headers using Vertex AI auth""" from litellm import vertex_chat_completion - _auth_header, vertex_project = ( - await vertex_chat_completion._ensure_access_token_async( - credentials=self.path_service_account_json, - project_id=None, - custom_llm_provider="vertex_ai", - ) + ( + _auth_header, + vertex_project, + ) = await vertex_chat_completion._ensure_access_token_async( + credentials=self.path_service_account_json, + project_id=None, + custom_llm_provider="vertex_ai", ) auth_header, _ = vertex_chat_completion._get_token_and_url( diff --git a/litellm/integrations/humanloop.py b/litellm/integrations/humanloop.py index fd3463f9e3..4651238af4 100644 --- a/litellm/integrations/humanloop.py +++ b/litellm/integrations/humanloop.py @@ -155,11 +155,7 @@ class HumanloopLogger(CustomLogger): prompt_id: str, prompt_variables: Optional[dict], dynamic_callback_params: StandardCallbackDynamicParams, - ) -> Tuple[ - str, - List[AllMessageValues], - dict, - ]: + ) -> Tuple[str, List[AllMessageValues], dict,]: humanloop_api_key = dynamic_callback_params.get( "humanloop_api_key" ) or get_secret_str("HUMANLOOP_API_KEY") diff --git a/litellm/integrations/langfuse/langfuse.py b/litellm/integrations/langfuse/langfuse.py index f990a316c4..d0472ee638 100644 --- a/litellm/integrations/langfuse/langfuse.py +++ b/litellm/integrations/langfuse/langfuse.py @@ -471,9 +471,9 @@ class LangFuseLogger: # we clean out all extra litellm metadata params before logging clean_metadata: Dict[str, Any] = {} if prompt_management_metadata is not None: - clean_metadata["prompt_management_metadata"] = ( - prompt_management_metadata - ) + clean_metadata[ + "prompt_management_metadata" + ] = prompt_management_metadata if isinstance(metadata, dict): for key, value in metadata.items(): # generate langfuse tags - Default Tags sent to Langfuse from LiteLLM Proxy diff --git a/litellm/integrations/langfuse/langfuse_handler.py b/litellm/integrations/langfuse/langfuse_handler.py index aebe1461b0..f9d27f6cf0 100644 --- a/litellm/integrations/langfuse/langfuse_handler.py +++ b/litellm/integrations/langfuse/langfuse_handler.py @@ -19,7 +19,6 @@ else: class LangFuseHandler: - @staticmethod def get_langfuse_logger_for_request( standard_callback_dynamic_params: StandardCallbackDynamicParams, @@ -87,7 +86,9 @@ class LangFuseHandler: if globalLangfuseLogger is not None: return globalLangfuseLogger - credentials_dict: Dict[str, Any] = ( + credentials_dict: Dict[ + str, Any + ] = ( {} ) # the global langfuse logger uses Environment Variables, there are no dynamic credentials globalLangfuseLogger = in_memory_dynamic_logger_cache.get_cache( diff --git a/litellm/integrations/langfuse/langfuse_prompt_management.py b/litellm/integrations/langfuse/langfuse_prompt_management.py index 1f4ca84db3..30f991ebd6 100644 --- a/litellm/integrations/langfuse/langfuse_prompt_management.py +++ b/litellm/integrations/langfuse/langfuse_prompt_management.py @@ -172,11 +172,7 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge prompt_id: str, prompt_variables: Optional[dict], dynamic_callback_params: StandardCallbackDynamicParams, - ) -> Tuple[ - str, - List[AllMessageValues], - dict, - ]: + ) -> Tuple[str, List[AllMessageValues], dict,]: return self.get_chat_completion_prompt( model, messages, diff --git a/litellm/integrations/langsmith.py b/litellm/integrations/langsmith.py index 1ef90c1822..0914150db9 100644 --- a/litellm/integrations/langsmith.py +++ b/litellm/integrations/langsmith.py @@ -75,7 +75,6 @@ class LangsmithLogger(CustomBatchLogger): langsmith_project: Optional[str] = None, langsmith_base_url: Optional[str] = None, ) -> LangsmithCredentialsObject: - _credentials_api_key = langsmith_api_key or os.getenv("LANGSMITH_API_KEY") if _credentials_api_key is None: raise Exception( @@ -443,9 +442,9 @@ class LangsmithLogger(CustomBatchLogger): Otherwise, use the default credentials. """ - standard_callback_dynamic_params: Optional[StandardCallbackDynamicParams] = ( - kwargs.get("standard_callback_dynamic_params", None) - ) + standard_callback_dynamic_params: Optional[ + StandardCallbackDynamicParams + ] = kwargs.get("standard_callback_dynamic_params", None) if standard_callback_dynamic_params is not None: credentials = self.get_credentials_from_env( langsmith_api_key=standard_callback_dynamic_params.get( @@ -481,7 +480,6 @@ class LangsmithLogger(CustomBatchLogger): asyncio.run(self.async_send_batch()) def get_run_by_id(self, run_id): - langsmith_api_key = self.default_credentials["LANGSMITH_API_KEY"] langsmith_api_base = self.default_credentials["LANGSMITH_BASE_URL"] diff --git a/litellm/integrations/langtrace.py b/litellm/integrations/langtrace.py index 51cd272ff1..ac1069f440 100644 --- a/litellm/integrations/langtrace.py +++ b/litellm/integrations/langtrace.py @@ -1,12 +1,12 @@ import json -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Union from litellm.proxy._types import SpanAttributes if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/integrations/lunary.py b/litellm/integrations/lunary.py index fcd781e44e..b24a24e088 100644 --- a/litellm/integrations/lunary.py +++ b/litellm/integrations/lunary.py @@ -20,7 +20,6 @@ def parse_tool_calls(tool_calls): return None def clean_tool_call(tool_call): - serialized = { "type": tool_call.type, "id": tool_call.id, @@ -36,7 +35,6 @@ def parse_tool_calls(tool_calls): def parse_messages(input): - if input is None: return None diff --git a/litellm/integrations/mlflow.py b/litellm/integrations/mlflow.py index 193d1c4ea2..e7a458accf 100644 --- a/litellm/integrations/mlflow.py +++ b/litellm/integrations/mlflow.py @@ -48,14 +48,17 @@ class MlflowLogger(CustomLogger): def _extract_and_set_chat_attributes(self, span, kwargs, response_obj): try: - from mlflow.tracing.utils import set_span_chat_messages, set_span_chat_tools + from mlflow.tracing.utils import set_span_chat_messages # type: ignore + from mlflow.tracing.utils import set_span_chat_tools # type: ignore except ImportError: return inputs = self._construct_input(kwargs) input_messages = inputs.get("messages", []) - output_messages = [c.message.model_dump(exclude_none=True) - for c in getattr(response_obj, "choices", [])] + output_messages = [ + c.message.model_dump(exclude_none=True) + for c in getattr(response_obj, "choices", []) + ] if messages := [*input_messages, *output_messages]: set_span_chat_messages(span, messages) if tools := inputs.get("tools"): diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py index 1572eb81f5..f4fe40738b 100644 --- a/litellm/integrations/opentelemetry.py +++ b/litellm/integrations/opentelemetry.py @@ -1,7 +1,7 @@ import os from dataclasses import dataclass from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast import litellm from litellm._logging import verbose_logger @@ -23,10 +23,10 @@ if TYPE_CHECKING: ) from litellm.proxy.proxy_server import UserAPIKeyAuth as _UserAPIKeyAuth - Span = _Span - SpanExporter = _SpanExporter - UserAPIKeyAuth = _UserAPIKeyAuth - ManagementEndpointLoggingPayload = _ManagementEndpointLoggingPayload + Span = Union[_Span, Any] + SpanExporter = Union[_SpanExporter, Any] + UserAPIKeyAuth = Union[_UserAPIKeyAuth, Any] + ManagementEndpointLoggingPayload = Union[_ManagementEndpointLoggingPayload, Any] else: Span = Any SpanExporter = Any @@ -46,7 +46,6 @@ LITELLM_REQUEST_SPAN_NAME = "litellm_request" @dataclass class OpenTelemetryConfig: - exporter: Union[str, SpanExporter] = "console" endpoint: Optional[str] = None headers: Optional[str] = None @@ -154,7 +153,6 @@ class OpenTelemetry(CustomLogger): end_time: Optional[Union[datetime, float]] = None, event_metadata: Optional[dict] = None, ): - from opentelemetry import trace from opentelemetry.trace import Status, StatusCode @@ -215,7 +213,6 @@ class OpenTelemetry(CustomLogger): end_time: Optional[Union[float, datetime]] = None, event_metadata: Optional[dict] = None, ): - from opentelemetry import trace from opentelemetry.trace import Status, StatusCode @@ -353,9 +350,9 @@ class OpenTelemetry(CustomLogger): """ from opentelemetry import trace - standard_callback_dynamic_params: Optional[StandardCallbackDynamicParams] = ( - kwargs.get("standard_callback_dynamic_params") - ) + standard_callback_dynamic_params: Optional[ + StandardCallbackDynamicParams + ] = kwargs.get("standard_callback_dynamic_params") if not standard_callback_dynamic_params: return @@ -722,7 +719,6 @@ class OpenTelemetry(CustomLogger): span.set_attribute(key, primitive_value) def set_raw_request_attributes(self, span: Span, kwargs, response_obj): - kwargs.get("optional_params", {}) litellm_params = kwargs.get("litellm_params", {}) or {} custom_llm_provider = litellm_params.get("custom_llm_provider", "Unknown") @@ -843,12 +839,14 @@ class OpenTelemetry(CustomLogger): headers=dynamic_headers or self.OTEL_HEADERS ) - if isinstance(self.OTEL_EXPORTER, SpanExporter): + if hasattr( + self.OTEL_EXPORTER, "export" + ): # Check if it has the export method that SpanExporter requires verbose_logger.debug( "OpenTelemetry: intiializing SpanExporter. Value of OTEL_EXPORTER: %s", self.OTEL_EXPORTER, ) - return SimpleSpanProcessor(self.OTEL_EXPORTER) + return SimpleSpanProcessor(cast(SpanExporter, self.OTEL_EXPORTER)) if self.OTEL_EXPORTER == "console": verbose_logger.debug( @@ -907,7 +905,6 @@ class OpenTelemetry(CustomLogger): logging_payload: ManagementEndpointLoggingPayload, parent_otel_span: Optional[Span] = None, ): - from opentelemetry import trace from opentelemetry.trace import Status, StatusCode @@ -961,7 +958,6 @@ class OpenTelemetry(CustomLogger): logging_payload: ManagementEndpointLoggingPayload, parent_otel_span: Optional[Span] = None, ): - from opentelemetry import trace from opentelemetry.trace import Status, StatusCode diff --git a/litellm/integrations/opik/opik.py b/litellm/integrations/opik/opik.py index 1f7f18f336..8cbfb9e653 100644 --- a/litellm/integrations/opik/opik.py +++ b/litellm/integrations/opik/opik.py @@ -185,7 +185,6 @@ class OpikLogger(CustomBatchLogger): def _create_opik_payload( # noqa: PLR0915 self, kwargs, response_obj, start_time, end_time ) -> List[Dict]: - # Get metadata _litellm_params = kwargs.get("litellm_params", {}) or {} litellm_params_metadata = _litellm_params.get("metadata", {}) or {} diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py index d6e47b87ce..5ac8c80eb3 100644 --- a/litellm/integrations/prometheus.py +++ b/litellm/integrations/prometheus.py @@ -988,9 +988,9 @@ class PrometheusLogger(CustomLogger): ): try: verbose_logger.debug("setting remaining tokens requests metric") - standard_logging_payload: Optional[StandardLoggingPayload] = ( - request_kwargs.get("standard_logging_object") - ) + standard_logging_payload: Optional[ + StandardLoggingPayload + ] = request_kwargs.get("standard_logging_object") if standard_logging_payload is None: return diff --git a/litellm/integrations/prompt_management_base.py b/litellm/integrations/prompt_management_base.py index 3fe3b31ed8..07b6720ffe 100644 --- a/litellm/integrations/prompt_management_base.py +++ b/litellm/integrations/prompt_management_base.py @@ -14,7 +14,6 @@ class PromptManagementClient(TypedDict): class PromptManagementBase(ABC): - @property @abstractmethod def integration_name(self) -> str: @@ -83,11 +82,7 @@ class PromptManagementBase(ABC): prompt_id: str, prompt_variables: Optional[dict], dynamic_callback_params: StandardCallbackDynamicParams, - ) -> Tuple[ - str, - List[AllMessageValues], - dict, - ]: + ) -> Tuple[str, List[AllMessageValues], dict,]: if not self.should_run_prompt_management( prompt_id=prompt_id, dynamic_callback_params=dynamic_callback_params ): diff --git a/litellm/integrations/s3.py b/litellm/integrations/s3.py index 4a0c27354f..01b9248e03 100644 --- a/litellm/integrations/s3.py +++ b/litellm/integrations/s3.py @@ -38,7 +38,7 @@ class S3Logger: if litellm.s3_callback_params is not None: # read in .env variables - example os.environ/AWS_BUCKET_NAME for key, value in litellm.s3_callback_params.items(): - if type(value) is str and value.startswith("os.environ/"): + if isinstance(value, str) and value.startswith("os.environ/"): litellm.s3_callback_params[key] = litellm.get_secret(value) # now set s3 params from litellm.s3_logger_params s3_bucket_name = litellm.s3_callback_params.get("s3_bucket_name") diff --git a/litellm/integrations/weights_biases.py b/litellm/integrations/weights_biases.py index 5fcbab04b3..63d87c9bd9 100644 --- a/litellm/integrations/weights_biases.py +++ b/litellm/integrations/weights_biases.py @@ -21,11 +21,11 @@ try: # contains a (known) object attribute object: Literal["chat.completion", "edit", "text_completion"] - def __getitem__(self, key: K) -> V: ... # noqa + def __getitem__(self, key: K) -> V: + ... # noqa - def get( # noqa - self, key: K, default: Optional[V] = None - ) -> Optional[V]: ... # pragma: no cover + def get(self, key: K, default: Optional[V] = None) -> Optional[V]: # noqa + ... # pragma: no cover class OpenAIRequestResponseResolver: def __call__( diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index 2036b93692..275c53ad30 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -10,7 +10,7 @@ from litellm.types.llms.openai import AllMessageValues if TYPE_CHECKING: from opentelemetry.trace import Span as _Span - Span = _Span + Span = Union[_Span, Any] else: Span = Any diff --git a/litellm/litellm_core_utils/default_encoding.py b/litellm/litellm_core_utils/default_encoding.py index 05bf78a6a9..93b3132912 100644 --- a/litellm/litellm_core_utils/default_encoding.py +++ b/litellm/litellm_core_utils/default_encoding.py @@ -11,7 +11,9 @@ except (ImportError, AttributeError): # Old way to access resources, which setuptools deprecated some time ago import pkg_resources # type: ignore - filename = pkg_resources.resource_filename(__name__, "litellm_core_utils/tokenizers") + filename = pkg_resources.resource_filename( + __name__, "litellm_core_utils/tokenizers" + ) os.environ["TIKTOKEN_CACHE_DIR"] = os.getenv( "CUSTOM_TIKTOKEN_CACHE_DIR", filename diff --git a/litellm/litellm_core_utils/get_supported_openai_params.py b/litellm/litellm_core_utils/get_supported_openai_params.py index 3d4f8cef6f..ccbdb331fd 100644 --- a/litellm/litellm_core_utils/get_supported_openai_params.py +++ b/litellm/litellm_core_utils/get_supported_openai_params.py @@ -79,6 +79,22 @@ def get_supported_openai_params( # noqa: PLR0915 elif custom_llm_provider == "maritalk": return litellm.MaritalkConfig().get_supported_openai_params(model=model) elif custom_llm_provider == "openai": + if request_type == "transcription": + transcription_provider_config = ( + litellm.ProviderConfigManager.get_provider_audio_transcription_config( + model=model, provider=LlmProviders.OPENAI + ) + ) + if isinstance( + transcription_provider_config, litellm.OpenAIGPTAudioTranscriptionConfig + ): + return transcription_provider_config.get_supported_openai_params( + model=model + ) + else: + raise ValueError( + f"Unsupported provider config: {transcription_provider_config} for model: {model}" + ) return litellm.OpenAIConfig().get_supported_openai_params(model=model) elif custom_llm_provider == "azure": if litellm.AzureOpenAIO1Config().is_o_series_model(model=model): diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 3565c4468c..edb37b33a8 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -67,6 +67,7 @@ from litellm.types.utils import ( StandardCallbackDynamicParams, StandardLoggingAdditionalHeaders, StandardLoggingHiddenParams, + StandardLoggingMCPToolCall, StandardLoggingMetadata, StandardLoggingModelCostFailureDebugInformation, StandardLoggingModelInformation, @@ -239,9 +240,9 @@ class Logging(LiteLLMLoggingBaseClass): self.litellm_trace_id = litellm_trace_id self.function_id = function_id self.streaming_chunks: List[Any] = [] # for generating complete stream response - self.sync_streaming_chunks: List[Any] = ( - [] - ) # for generating complete stream response + self.sync_streaming_chunks: List[ + Any + ] = [] # for generating complete stream response self.log_raw_request_response = log_raw_request_response # Initialize dynamic callbacks @@ -452,18 +453,19 @@ class Logging(LiteLLMLoggingBaseClass): prompt_id: str, prompt_variables: Optional[dict], ) -> Tuple[str, List[AllMessageValues], dict]: - custom_logger = self.get_custom_logger_for_prompt_management(model) if custom_logger: - model, messages, non_default_params = ( - custom_logger.get_chat_completion_prompt( - model=model, - messages=messages, - non_default_params=non_default_params, - prompt_id=prompt_id, - prompt_variables=prompt_variables, - dynamic_callback_params=self.standard_callback_dynamic_params, - ) + ( + model, + messages, + non_default_params, + ) = custom_logger.get_chat_completion_prompt( + model=model, + messages=messages, + non_default_params=non_default_params, + prompt_id=prompt_id, + prompt_variables=prompt_variables, + dynamic_callback_params=self.standard_callback_dynamic_params, ) self.messages = messages return model, messages, non_default_params @@ -541,12 +543,11 @@ class Logging(LiteLLMLoggingBaseClass): model ): # if model name was changes pre-call, overwrite the initial model call name with the new one self.model_call_details["model"] = model - self.model_call_details["litellm_params"]["api_base"] = ( - self._get_masked_api_base(additional_args.get("api_base", "")) - ) + self.model_call_details["litellm_params"][ + "api_base" + ] = self._get_masked_api_base(additional_args.get("api_base", "")) def pre_call(self, input, api_key, model=None, additional_args={}): # noqa: PLR0915 - # Log the exact input to the LLM API litellm.error_logs["PRE_CALL"] = locals() try: @@ -568,19 +569,16 @@ class Logging(LiteLLMLoggingBaseClass): self.log_raw_request_response is True or log_raw_request_response is True ): - _litellm_params = self.model_call_details.get("litellm_params", {}) _metadata = _litellm_params.get("metadata", {}) or {} try: # [Non-blocking Extra Debug Information in metadata] if turn_off_message_logging is True: - - _metadata["raw_request"] = ( - "redacted by litellm. \ + _metadata[ + "raw_request" + ] = "redacted by litellm. \ 'litellm.turn_off_message_logging=True'" - ) else: - curl_command = self._get_request_curl_command( api_base=additional_args.get("api_base", ""), headers=additional_args.get("headers", {}), @@ -590,33 +588,33 @@ class Logging(LiteLLMLoggingBaseClass): _metadata["raw_request"] = str(curl_command) # split up, so it's easier to parse in the UI - self.model_call_details["raw_request_typed_dict"] = ( - RawRequestTypedDict( - raw_request_api_base=str( - additional_args.get("api_base") or "" - ), - raw_request_body=self._get_raw_request_body( - additional_args.get("complete_input_dict", {}) - ), - raw_request_headers=self._get_masked_headers( - additional_args.get("headers", {}) or {}, - ignore_sensitive_headers=True, - ), - error=None, - ) + self.model_call_details[ + "raw_request_typed_dict" + ] = RawRequestTypedDict( + raw_request_api_base=str( + additional_args.get("api_base") or "" + ), + raw_request_body=self._get_raw_request_body( + additional_args.get("complete_input_dict", {}) + ), + raw_request_headers=self._get_masked_headers( + additional_args.get("headers", {}) or {}, + ignore_sensitive_headers=True, + ), + error=None, ) except Exception as e: - self.model_call_details["raw_request_typed_dict"] = ( - RawRequestTypedDict( - error=str(e), - ) + self.model_call_details[ + "raw_request_typed_dict" + ] = RawRequestTypedDict( + error=str(e), ) traceback.print_exc() - _metadata["raw_request"] = ( - "Unable to Log \ + _metadata[ + "raw_request" + ] = "Unable to Log \ raw request: {}".format( - str(e) - ) + str(e) ) if self.logger_fn and callable(self.logger_fn): try: @@ -941,9 +939,9 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( f"response_cost_failure_debug_information: {debug_info}" ) - self.model_call_details["response_cost_failure_debug_information"] = ( - debug_info - ) + self.model_call_details[ + "response_cost_failure_debug_information" + ] = debug_info return None try: @@ -968,9 +966,9 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( f"response_cost_failure_debug_information: {debug_info}" ) - self.model_call_details["response_cost_failure_debug_information"] = ( - debug_info - ) + self.model_call_details[ + "response_cost_failure_debug_information" + ] = debug_info return None @@ -995,7 +993,6 @@ class Logging(LiteLLMLoggingBaseClass): def should_run_callback( self, callback: litellm.CALLBACK_TYPES, litellm_params: dict, event_hook: str ) -> bool: - if litellm.global_disable_no_log_param: return True @@ -1027,9 +1024,9 @@ class Logging(LiteLLMLoggingBaseClass): end_time = datetime.datetime.now() if self.completion_start_time is None: self.completion_start_time = end_time - self.model_call_details["completion_start_time"] = ( - self.completion_start_time - ) + self.model_call_details[ + "completion_start_time" + ] = self.completion_start_time self.model_call_details["log_event_type"] = "successful_api_call" self.model_call_details["end_time"] = end_time self.model_call_details["cache_hit"] = cache_hit @@ -1083,39 +1080,39 @@ class Logging(LiteLLMLoggingBaseClass): "response_cost" ] else: - self.model_call_details["response_cost"] = ( - self._response_cost_calculator(result=result) - ) + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator(result=result) ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=result, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=result, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) - elif isinstance(result, dict): # pass-through endpoints + elif isinstance(result, dict) or isinstance(result, list): ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=result, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=result, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) elif standard_logging_object is not None: - self.model_call_details["standard_logging_object"] = ( - standard_logging_object - ) + self.model_call_details[ + "standard_logging_object" + ] = standard_logging_object else: # streaming chunks + image gen. self.model_call_details["response_cost"] = None @@ -1154,7 +1151,6 @@ class Logging(LiteLLMLoggingBaseClass): standard_logging_object=kwargs.get("standard_logging_object", None), ) try: - ## BUILD COMPLETE STREAMED RESPONSE complete_streaming_response: Optional[ Union[ModelResponse, TextCompletionResponse, ResponsesAPIResponse] @@ -1172,23 +1168,23 @@ class Logging(LiteLLMLoggingBaseClass): verbose_logger.debug( "Logging Details LiteLLM-Success Call streaming complete" ) - self.model_call_details["complete_streaming_response"] = ( - complete_streaming_response - ) - self.model_call_details["response_cost"] = ( - self._response_cost_calculator(result=complete_streaming_response) - ) + self.model_call_details[ + "complete_streaming_response" + ] = complete_streaming_response + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator(result=complete_streaming_response) ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=complete_streaming_response, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=complete_streaming_response, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) callbacks = self.get_combined_callback_list( dynamic_success_callbacks=self.dynamic_success_callbacks, @@ -1207,7 +1203,6 @@ class Logging(LiteLLMLoggingBaseClass): ## LOGGING HOOK ## for callback in callbacks: if isinstance(callback, CustomLogger): - self.model_call_details, result = callback.logging_hook( kwargs=self.model_call_details, result=result, @@ -1538,10 +1533,10 @@ class Logging(LiteLLMLoggingBaseClass): ) else: if self.stream and complete_streaming_response: - self.model_call_details["complete_response"] = ( - self.model_call_details.get( - "complete_streaming_response", {} - ) + self.model_call_details[ + "complete_response" + ] = self.model_call_details.get( + "complete_streaming_response", {} ) result = self.model_call_details["complete_response"] openMeterLogger.log_success_event( @@ -1581,10 +1576,10 @@ class Logging(LiteLLMLoggingBaseClass): ) else: if self.stream and complete_streaming_response: - self.model_call_details["complete_response"] = ( - self.model_call_details.get( - "complete_streaming_response", {} - ) + self.model_call_details[ + "complete_response" + ] = self.model_call_details.get( + "complete_streaming_response", {} ) result = self.model_call_details["complete_response"] @@ -1659,7 +1654,6 @@ class Logging(LiteLLMLoggingBaseClass): if self.call_type == CallTypes.aretrieve_batch.value and isinstance( result, LiteLLMBatch ): - response_cost, batch_usage, batch_models = await _handle_completed_batch( batch=result, custom_llm_provider=self.custom_llm_provider ) @@ -1692,9 +1686,9 @@ class Logging(LiteLLMLoggingBaseClass): if complete_streaming_response is not None: print_verbose("Async success callbacks: Got a complete streaming response") - self.model_call_details["async_complete_streaming_response"] = ( - complete_streaming_response - ) + self.model_call_details[ + "async_complete_streaming_response" + ] = complete_streaming_response try: if self.model_call_details.get("cache_hit", False) is True: self.model_call_details["response_cost"] = 0.0 @@ -1704,10 +1698,10 @@ class Logging(LiteLLMLoggingBaseClass): model_call_details=self.model_call_details ) # base_model defaults to None if not set on model_info - self.model_call_details["response_cost"] = ( - self._response_cost_calculator( - result=complete_streaming_response - ) + self.model_call_details[ + "response_cost" + ] = self._response_cost_calculator( + result=complete_streaming_response ) verbose_logger.debug( @@ -1720,16 +1714,16 @@ class Logging(LiteLLMLoggingBaseClass): self.model_call_details["response_cost"] = None ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj=complete_streaming_response, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="success", - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj=complete_streaming_response, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="success", + standard_built_in_tools_params=self.standard_built_in_tools_params, ) callbacks = self.get_combined_callback_list( dynamic_success_callbacks=self.dynamic_async_success_callbacks, @@ -1935,18 +1929,18 @@ class Logging(LiteLLMLoggingBaseClass): ## STANDARDIZED LOGGING PAYLOAD - self.model_call_details["standard_logging_object"] = ( - get_standard_logging_object_payload( - kwargs=self.model_call_details, - init_response_obj={}, - start_time=start_time, - end_time=end_time, - logging_obj=self, - status="failure", - error_str=str(exception), - original_exception=exception, - standard_built_in_tools_params=self.standard_built_in_tools_params, - ) + self.model_call_details[ + "standard_logging_object" + ] = get_standard_logging_object_payload( + kwargs=self.model_call_details, + init_response_obj={}, + start_time=start_time, + end_time=end_time, + logging_obj=self, + status="failure", + error_str=str(exception), + original_exception=exception, + standard_built_in_tools_params=self.standard_built_in_tools_params, ) return start_time, end_time @@ -2084,7 +2078,6 @@ class Logging(LiteLLMLoggingBaseClass): ) is not True ): # custom logger class - callback.log_failure_event( start_time=start_time, end_time=end_time, @@ -2713,9 +2706,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 endpoint=arize_config.endpoint, ) - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - f"space_key={arize_config.space_key},api_key={arize_config.api_key}" - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = f"space_key={arize_config.space_key},api_key={arize_config.api_key}" for callback in _in_memory_loggers: if ( isinstance(callback, ArizeLogger) @@ -2739,9 +2732,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 # auth can be disabled on local deployments of arize phoenix if arize_phoenix_config.otlp_auth_headers is not None: - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - arize_phoenix_config.otlp_auth_headers - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = arize_phoenix_config.otlp_auth_headers for callback in _in_memory_loggers: if ( @@ -2832,9 +2825,9 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915 exporter="otlp_http", endpoint="https://langtrace.ai/api/trace", ) - os.environ["OTEL_EXPORTER_OTLP_TRACES_HEADERS"] = ( - f"api_key={os.getenv('LANGTRACE_API_KEY')}" - ) + os.environ[ + "OTEL_EXPORTER_OTLP_TRACES_HEADERS" + ] = f"api_key={os.getenv('LANGTRACE_API_KEY')}" for callback in _in_memory_loggers: if ( isinstance(callback, OpenTelemetry) @@ -3114,6 +3107,7 @@ class StandardLoggingPayloadSetup: litellm_params: Optional[dict] = None, prompt_integration: Optional[str] = None, applied_guardrails: Optional[List[str]] = None, + mcp_tool_call_metadata: Optional[StandardLoggingMCPToolCall] = None, ) -> StandardLoggingMetadata: """ Clean and filter the metadata dictionary to include only the specified keys in StandardLoggingMetadata. @@ -3160,6 +3154,7 @@ class StandardLoggingPayloadSetup: user_api_key_end_user_id=None, prompt_management_metadata=prompt_management_metadata, applied_guardrails=applied_guardrails, + mcp_tool_call_metadata=mcp_tool_call_metadata, ) if isinstance(metadata, dict): # Filter the metadata dictionary to include only the specified keys @@ -3223,7 +3218,6 @@ class StandardLoggingPayloadSetup: custom_llm_provider: Optional[str], init_response_obj: Union[Any, BaseModel, dict], ) -> StandardLoggingModelInformation: - model_cost_name = _select_model_name_for_cost_calc( model=None, completion_response=init_response_obj, # type: ignore @@ -3286,7 +3280,6 @@ class StandardLoggingPayloadSetup: def get_additional_headers( additiona_headers: Optional[dict], ) -> Optional[StandardLoggingAdditionalHeaders]: - if additiona_headers is None: return None @@ -3322,10 +3315,10 @@ class StandardLoggingPayloadSetup: for key in StandardLoggingHiddenParams.__annotations__.keys(): if key in hidden_params: if key == "additional_headers": - clean_hidden_params["additional_headers"] = ( - StandardLoggingPayloadSetup.get_additional_headers( - hidden_params[key] - ) + clean_hidden_params[ + "additional_headers" + ] = StandardLoggingPayloadSetup.get_additional_headers( + hidden_params[key] ) else: clean_hidden_params[key] = hidden_params[key] # type: ignore @@ -3463,12 +3456,14 @@ def get_standard_logging_object_payload( ) # cleanup timestamps - start_time_float, end_time_float, completion_start_time_float = ( - StandardLoggingPayloadSetup.cleanup_timestamps( - start_time=start_time, - end_time=end_time, - completion_start_time=completion_start_time, - ) + ( + start_time_float, + end_time_float, + completion_start_time_float, + ) = StandardLoggingPayloadSetup.cleanup_timestamps( + start_time=start_time, + end_time=end_time, + completion_start_time=completion_start_time, ) response_time = StandardLoggingPayloadSetup.get_response_time( start_time_float=start_time_float, @@ -3486,6 +3481,7 @@ def get_standard_logging_object_payload( litellm_params=litellm_params, prompt_integration=kwargs.get("prompt_integration", None), applied_guardrails=kwargs.get("applied_guardrails", None), + mcp_tool_call_metadata=kwargs.get("mcp_tool_call_metadata", None), ) _request_body = proxy_server_request.get("body", {}) @@ -3495,7 +3491,6 @@ def get_standard_logging_object_payload( saved_cache_cost: float = 0.0 if cache_hit is True: - id = f"{id}_cache_hit{time.time()}" # do not duplicate the request id saved_cache_cost = ( logging_obj._response_cost_calculator( @@ -3626,6 +3621,7 @@ def get_standard_logging_metadata( user_api_key_end_user_id=None, prompt_management_metadata=None, applied_guardrails=None, + mcp_tool_call_metadata=None, ) if isinstance(metadata, dict): # Filter the metadata dictionary to include only the specified keys @@ -3658,9 +3654,9 @@ def scrub_sensitive_keys_in_metadata(litellm_params: Optional[dict]): ): for k, v in metadata["user_api_key_metadata"].items(): if k == "logging": # prevent logging user logging keys - cleaned_user_api_key_metadata[k] = ( - "scrubbed_by_litellm_for_sensitive_keys" - ) + cleaned_user_api_key_metadata[ + k + ] = "scrubbed_by_litellm_for_sensitive_keys" else: cleaned_user_api_key_metadata[k] = v diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index 7af3a26d2e..caccbc4bac 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -1,7 +1,7 @@ # What is this? ## Helper utilities for cost_per_token() -from typing import Optional, Tuple +from typing import Optional, Tuple, cast import litellm from litellm import verbose_logger @@ -121,6 +121,31 @@ def _get_completion_token_base_cost(model_info: ModelInfo, usage: Usage) -> floa return model_info["output_cost_per_token"] +def calculate_cost_component( + model_info: ModelInfo, cost_key: str, usage_value: Optional[float] +) -> float: + """ + Generic cost calculator for any usage component + + Args: + model_info: Dictionary containing cost information + cost_key: The key for the cost multiplier in model_info (e.g., 'input_cost_per_audio_token') + usage_value: The actual usage value (e.g., number of tokens, characters, seconds) + + Returns: + float: The calculated cost + """ + cost_per_unit = model_info.get(cost_key) + if ( + cost_per_unit is not None + and isinstance(cost_per_unit, float) + and usage_value is not None + and usage_value > 0 + ): + return float(usage_value) * cost_per_unit + return 0.0 + + def generic_cost_per_token( model: str, usage: Usage, custom_llm_provider: str ) -> Tuple[float, float]: @@ -136,6 +161,7 @@ def generic_cost_per_token( Returns: Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd """ + ## GET MODEL INFO model_info = get_model_info(model=model, custom_llm_provider=custom_llm_provider) @@ -143,38 +169,124 @@ def generic_cost_per_token( ### Cost of processing (non-cache hit + cache hit) + Cost of cache-writing (cache writing) prompt_cost = 0.0 ### PROCESSING COST - non_cache_hit_tokens = usage.prompt_tokens + text_tokens = usage.prompt_tokens cache_hit_tokens = 0 - if usage.prompt_tokens_details and usage.prompt_tokens_details.cached_tokens: - cache_hit_tokens = usage.prompt_tokens_details.cached_tokens - non_cache_hit_tokens = non_cache_hit_tokens - cache_hit_tokens + audio_tokens = 0 + character_count = 0 + image_count = 0 + video_length_seconds = 0 + if usage.prompt_tokens_details: + cache_hit_tokens = ( + cast( + Optional[int], getattr(usage.prompt_tokens_details, "cached_tokens", 0) + ) + or 0 + ) + text_tokens = ( + cast( + Optional[int], getattr(usage.prompt_tokens_details, "text_tokens", None) + ) + or 0 # default to prompt tokens, if this field is not set + ) + audio_tokens = ( + cast(Optional[int], getattr(usage.prompt_tokens_details, "audio_tokens", 0)) + or 0 + ) + character_count = ( + cast( + Optional[int], + getattr(usage.prompt_tokens_details, "character_count", 0), + ) + or 0 + ) + image_count = ( + cast(Optional[int], getattr(usage.prompt_tokens_details, "image_count", 0)) + or 0 + ) + video_length_seconds = ( + cast( + Optional[int], + getattr(usage.prompt_tokens_details, "video_length_seconds", 0), + ) + or 0 + ) + + ## EDGE CASE - text tokens not set inside PromptTokensDetails + if text_tokens == 0: + text_tokens = usage.prompt_tokens - cache_hit_tokens - audio_tokens prompt_base_cost = _get_prompt_token_base_cost(model_info=model_info, usage=usage) - prompt_cost = float(non_cache_hit_tokens) * prompt_base_cost + prompt_cost = float(text_tokens) * prompt_base_cost - _cache_read_input_token_cost = model_info.get("cache_read_input_token_cost") - if ( - _cache_read_input_token_cost is not None - and usage.prompt_tokens_details - and usage.prompt_tokens_details.cached_tokens - ): - prompt_cost += ( - float(usage.prompt_tokens_details.cached_tokens) - * _cache_read_input_token_cost - ) + ### CACHE READ COST + prompt_cost += calculate_cost_component( + model_info, "cache_read_input_token_cost", cache_hit_tokens + ) + + ### AUDIO COST + prompt_cost += calculate_cost_component( + model_info, "input_cost_per_audio_token", audio_tokens + ) ### CACHE WRITING COST - _cache_creation_input_token_cost = model_info.get("cache_creation_input_token_cost") - if _cache_creation_input_token_cost is not None: - prompt_cost += ( - float(usage._cache_creation_input_tokens) * _cache_creation_input_token_cost - ) + prompt_cost += calculate_cost_component( + model_info, + "cache_creation_input_token_cost", + usage._cache_creation_input_tokens, + ) + + ### CHARACTER COST + + prompt_cost += calculate_cost_component( + model_info, "input_cost_per_character", character_count + ) + + ### IMAGE COUNT COST + prompt_cost += calculate_cost_component( + model_info, "input_cost_per_image", image_count + ) + + ### VIDEO LENGTH COST + prompt_cost += calculate_cost_component( + model_info, "input_cost_per_video_per_second", video_length_seconds + ) ## CALCULATE OUTPUT COST completion_base_cost = _get_completion_token_base_cost( model_info=model_info, usage=usage ) - completion_cost = usage["completion_tokens"] * completion_base_cost + text_tokens = usage.completion_tokens + audio_tokens = 0 + if usage.completion_tokens_details is not None: + audio_tokens = ( + cast( + Optional[int], + getattr(usage.completion_tokens_details, "audio_tokens", 0), + ) + or 0 + ) + text_tokens = ( + cast( + Optional[int], + getattr(usage.completion_tokens_details, "text_tokens", None), + ) + or usage.completion_tokens # default to completion tokens, if this field is not set + ) + + ## TEXT COST + completion_cost = float(text_tokens) * completion_base_cost + + _output_cost_per_audio_token: Optional[float] = model_info.get( + "output_cost_per_audio_token" + ) + + ## AUDIO COST + if ( + _output_cost_per_audio_token is not None + and audio_tokens is not None + and audio_tokens > 0 + ): + completion_cost += float(audio_tokens) * _output_cost_per_audio_token return prompt_cost, completion_cost diff --git a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py index d33af2a477..f3f4ce6ef4 100644 --- a/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py +++ b/litellm/litellm_core_utils/llm_response_utils/convert_dict_to_response.py @@ -258,14 +258,12 @@ def _extract_reasoning_content(message: dict) -> Tuple[Optional[str], Optional[s class LiteLLMResponseObjectHandler: - @staticmethod def convert_to_image_response( response_object: dict, model_response_object: Optional[ImageResponse] = None, hidden_params: Optional[dict] = None, ) -> ImageResponse: - response_object.update({"hidden_params": hidden_params}) if model_response_object is None: @@ -481,9 +479,9 @@ def convert_to_model_response_object( # noqa: PLR0915 provider_specific_fields["thinking_blocks"] = thinking_blocks if reasoning_content: - provider_specific_fields["reasoning_content"] = ( - reasoning_content - ) + provider_specific_fields[ + "reasoning_content" + ] = reasoning_content message = Message( content=content, diff --git a/litellm/litellm_core_utils/model_param_helper.py b/litellm/litellm_core_utils/model_param_helper.py index b7d8fc19d1..c96b4a3f5b 100644 --- a/litellm/litellm_core_utils/model_param_helper.py +++ b/litellm/litellm_core_utils/model_param_helper.py @@ -17,7 +17,6 @@ from litellm.types.rerank import RerankRequest class ModelParamHelper: - @staticmethod def get_standard_logging_model_parameters( model_parameters: dict, diff --git a/litellm/litellm_core_utils/prompt_templates/common_utils.py b/litellm/litellm_core_utils/prompt_templates/common_utils.py index c8745f5119..4170d3c1e1 100644 --- a/litellm/litellm_core_utils/prompt_templates/common_utils.py +++ b/litellm/litellm_core_utils/prompt_templates/common_utils.py @@ -257,7 +257,6 @@ def _insert_assistant_continue_message( and message.get("role") == "user" # Current is user and messages[i + 1].get("role") == "user" ): # Next is user - # Insert assistant message continue_message = ( assistant_continue_message or DEFAULT_ASSISTANT_CONTINUE_MESSAGE diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py index 28e09d7ac8..3c89141b5e 100644 --- a/litellm/litellm_core_utils/prompt_templates/factory.py +++ b/litellm/litellm_core_utils/prompt_templates/factory.py @@ -1042,10 +1042,10 @@ def convert_to_gemini_tool_call_invoke( if tool_calls is not None: for tool in tool_calls: if "function" in tool: - gemini_function_call: Optional[VertexFunctionCall] = ( - _gemini_tool_call_invoke_helper( - function_call_params=tool["function"] - ) + gemini_function_call: Optional[ + VertexFunctionCall + ] = _gemini_tool_call_invoke_helper( + function_call_params=tool["function"] ) if gemini_function_call is not None: _parts_list.append( @@ -1432,9 +1432,9 @@ def anthropic_messages_pt( # noqa: PLR0915 ) if "cache_control" in _content_element: - _anthropic_content_element["cache_control"] = ( - _content_element["cache_control"] - ) + _anthropic_content_element[ + "cache_control" + ] = _content_element["cache_control"] user_content.append(_anthropic_content_element) elif m.get("type", "") == "text": m = cast(ChatCompletionTextObject, m) @@ -1466,9 +1466,9 @@ def anthropic_messages_pt( # noqa: PLR0915 ) if "cache_control" in _content_element: - _anthropic_content_text_element["cache_control"] = ( - _content_element["cache_control"] - ) + _anthropic_content_text_element[ + "cache_control" + ] = _content_element["cache_control"] user_content.append(_anthropic_content_text_element) @@ -1533,7 +1533,6 @@ def anthropic_messages_pt( # noqa: PLR0915 "content" ] # don't pass empty text blocks. anthropic api raises errors. ): - _anthropic_text_content_element = AnthropicMessagesTextParam( type="text", text=assistant_content_block["content"], @@ -1569,7 +1568,6 @@ def anthropic_messages_pt( # noqa: PLR0915 msg_i += 1 if assistant_content: - new_messages.append({"role": "assistant", "content": assistant_content}) if msg_i == init_msg_i: # prevent infinite loops @@ -2245,7 +2243,6 @@ class BedrockImageProcessor: @staticmethod async def get_image_details_async(image_url) -> Tuple[str, str]: try: - client = get_async_httpx_client( llm_provider=httpxSpecialProvider.PromptFactory, params={"concurrent_limit": 1}, @@ -2612,7 +2609,6 @@ def get_user_message_block_or_continue_message( for item in modified_content_block: # Check if the list is empty if item["type"] == "text": - if not item["text"].strip(): # Replace empty text with continue message _user_continue_message = ChatCompletionUserMessage( @@ -3207,7 +3203,6 @@ def _bedrock_converse_messages_pt( # noqa: PLR0915 assistant_content: List[BedrockContentBlock] = [] ## MERGE CONSECUTIVE ASSISTANT CONTENT ## while msg_i < len(messages) and messages[msg_i]["role"] == "assistant": - assistant_message_block = get_assistant_message_block_or_continue_message( message=messages[msg_i], assistant_continue_message=assistant_continue_message, @@ -3410,7 +3405,6 @@ def response_schema_prompt(model: str, response_schema: dict) -> str: {"role": "user", "content": "{}".format(response_schema)} ] if f"{model}/response_schema_prompt" in litellm.custom_prompt_dict: - custom_prompt_details = litellm.custom_prompt_dict[ f"{model}/response_schema_prompt" ] # allow user to define custom response schema prompt by model diff --git a/litellm/litellm_core_utils/realtime_streaming.py b/litellm/litellm_core_utils/realtime_streaming.py index aebd049692..e84c720441 100644 --- a/litellm/litellm_core_utils/realtime_streaming.py +++ b/litellm/litellm_core_utils/realtime_streaming.py @@ -122,7 +122,6 @@ class RealTimeStreaming: pass async def bidirectional_forward(self): - forward_task = asyncio.create_task(self.backend_to_client_send_messages()) try: await self.client_ack_messages() diff --git a/litellm/litellm_core_utils/redact_messages.py b/litellm/litellm_core_utils/redact_messages.py index 50e0e0b575..a62031a9c9 100644 --- a/litellm/litellm_core_utils/redact_messages.py +++ b/litellm/litellm_core_utils/redact_messages.py @@ -135,9 +135,9 @@ def _get_turn_off_message_logging_from_dynamic_params( handles boolean and string values of `turn_off_message_logging` """ - standard_callback_dynamic_params: Optional[StandardCallbackDynamicParams] = ( - model_call_details.get("standard_callback_dynamic_params", None) - ) + standard_callback_dynamic_params: Optional[ + StandardCallbackDynamicParams + ] = model_call_details.get("standard_callback_dynamic_params", None) if standard_callback_dynamic_params: _turn_off_message_logging = standard_callback_dynamic_params.get( "turn_off_message_logging" diff --git a/litellm/litellm_core_utils/safe_json_dumps.py b/litellm/litellm_core_utils/safe_json_dumps.py index 990c0ed561..7ad0038ecb 100644 --- a/litellm/litellm_core_utils/safe_json_dumps.py +++ b/litellm/litellm_core_utils/safe_json_dumps.py @@ -1,8 +1,9 @@ import json from typing import Any, Union +from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH -def safe_dumps(data: Any, max_depth: int = 10) -> str: +def safe_dumps(data: Any, max_depth: int = DEFAULT_MAX_RECURSE_DEPTH) -> str: """ Recursively serialize data while detecting circular references. If a circular reference is detected then a marker string is returned. diff --git a/litellm/litellm_core_utils/sensitive_data_masker.py b/litellm/litellm_core_utils/sensitive_data_masker.py index a1df115ff0..23b9ec32fc 100644 --- a/litellm/litellm_core_utils/sensitive_data_masker.py +++ b/litellm/litellm_core_utils/sensitive_data_masker.py @@ -1,5 +1,7 @@ from typing import Any, Dict, Optional, Set +from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH + class SensitiveDataMasker: def __init__( @@ -39,7 +41,10 @@ class SensitiveDataMasker: return result def mask_dict( - self, data: Dict[str, Any], depth: int = 0, max_depth: int = 10 + self, + data: Dict[str, Any], + depth: int = 0, + max_depth: int = DEFAULT_MAX_RECURSE_DEPTH, ) -> Dict[str, Any]: if depth >= max_depth: return data diff --git a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py index 7a5ee3e41e..1ca2bfe45e 100644 --- a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py +++ b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py @@ -104,7 +104,6 @@ class ChunkProcessor: def get_combined_tool_content( self, tool_call_chunks: List[Dict[str, Any]] ) -> List[ChatCompletionMessageToolCall]: - argument_list: List[str] = [] delta = tool_call_chunks[0]["choices"][0]["delta"] id = None diff --git a/litellm/litellm_core_utils/streaming_handler.py b/litellm/litellm_core_utils/streaming_handler.py index a11e5af12b..42106135cc 100644 --- a/litellm/litellm_core_utils/streaming_handler.py +++ b/litellm/litellm_core_utils/streaming_handler.py @@ -84,9 +84,9 @@ class CustomStreamWrapper: self.system_fingerprint: Optional[str] = None self.received_finish_reason: Optional[str] = None - self.intermittent_finish_reason: Optional[str] = ( - None # finish reasons that show up mid-stream - ) + self.intermittent_finish_reason: Optional[ + str + ] = None # finish reasons that show up mid-stream self.special_tokens = [ "<|assistant|>", "<|system|>", @@ -814,7 +814,6 @@ class CustomStreamWrapper: model_response: ModelResponseStream, response_obj: Dict[str, Any], ): - print_verbose( f"completion_obj: {completion_obj}, model_response.choices[0]: {model_response.choices[0]}, response_obj: {response_obj}" ) @@ -1008,7 +1007,6 @@ class CustomStreamWrapper: self.custom_llm_provider and self.custom_llm_provider in litellm._custom_providers ): - if self.received_finish_reason is not None: if "provider_specific_fields" not in chunk: raise StopIteration @@ -1379,9 +1377,9 @@ class CustomStreamWrapper: _json_delta = delta.model_dump() print_verbose(f"_json_delta: {_json_delta}") if "role" not in _json_delta or _json_delta["role"] is None: - _json_delta["role"] = ( - "assistant" # mistral's api returns role as None - ) + _json_delta[ + "role" + ] = "assistant" # mistral's api returns role as None if "tool_calls" in _json_delta and isinstance( _json_delta["tool_calls"], list ): @@ -1758,9 +1756,9 @@ class CustomStreamWrapper: chunk = next(self.completion_stream) if chunk is not None and chunk != b"": print_verbose(f"PROCESSED CHUNK PRE CHUNK CREATOR: {chunk}") - processed_chunk: Optional[ModelResponseStream] = ( - self.chunk_creator(chunk=chunk) - ) + processed_chunk: Optional[ + ModelResponseStream + ] = self.chunk_creator(chunk=chunk) print_verbose( f"PROCESSED CHUNK POST CHUNK CREATOR: {processed_chunk}" ) diff --git a/litellm/llms/anthropic/chat/handler.py b/litellm/llms/anthropic/chat/handler.py index f2c5f390d7..7625292e6e 100644 --- a/litellm/llms/anthropic/chat/handler.py +++ b/litellm/llms/anthropic/chat/handler.py @@ -290,7 +290,6 @@ class AnthropicChatCompletion(BaseLLM): headers={}, client=None, ): - optional_params = copy.deepcopy(optional_params) stream = optional_params.pop("stream", None) json_mode: bool = optional_params.pop("json_mode", False) @@ -491,7 +490,6 @@ class ModelResponseIterator: def _handle_usage( self, anthropic_usage_chunk: Union[dict, UsageDelta] ) -> AnthropicChatCompletionUsageBlock: - usage_block = AnthropicChatCompletionUsageBlock( prompt_tokens=anthropic_usage_chunk.get("input_tokens", 0), completion_tokens=anthropic_usage_chunk.get("output_tokens", 0), @@ -515,7 +513,9 @@ class ModelResponseIterator: return usage_block - def _content_block_delta_helper(self, chunk: dict) -> Tuple[ + def _content_block_delta_helper( + self, chunk: dict + ) -> Tuple[ str, Optional[ChatCompletionToolCallChunk], List[ChatCompletionThinkingBlock], @@ -592,9 +592,12 @@ class ModelResponseIterator: Anthropic content chunk chunk = {'type': 'content_block_delta', 'index': 0, 'delta': {'type': 'text_delta', 'text': 'Hello'}} """ - text, tool_use, thinking_blocks, provider_specific_fields = ( - self._content_block_delta_helper(chunk=chunk) - ) + ( + text, + tool_use, + thinking_blocks, + provider_specific_fields, + ) = self._content_block_delta_helper(chunk=chunk) if thinking_blocks: reasoning_content = self._handle_reasoning_content( thinking_blocks=thinking_blocks @@ -620,7 +623,6 @@ class ModelResponseIterator: "index": self.tool_index, } elif type_chunk == "content_block_stop": - ContentBlockStop(**chunk) # type: ignore # check if tool call content block is_empty = self.check_empty_tool_call_args() diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py index 1a77c453f4..0780f5b4d4 100644 --- a/litellm/llms/anthropic/chat/transformation.py +++ b/litellm/llms/anthropic/chat/transformation.py @@ -49,9 +49,9 @@ class AnthropicConfig(BaseConfig): to pass metadata to anthropic, it's {"user_id": "any-relevant-information"} """ - max_tokens: Optional[int] = ( - 4096 # anthropic requires a default value (Opus, Sonnet, and Haiku have the same default) - ) + max_tokens: Optional[ + int + ] = 4096 # anthropic requires a default value (Opus, Sonnet, and Haiku have the same default) stop_sequences: Optional[list] = None temperature: Optional[int] = None top_p: Optional[int] = None @@ -104,7 +104,6 @@ class AnthropicConfig(BaseConfig): def get_json_schema_from_pydantic_object( self, response_format: Union[Any, Dict, None] ) -> Optional[dict]: - return type_to_response_format_param( response_format, ref_template="/$defs/{model}" ) # Relevant issue: https://github.com/BerriAI/litellm/issues/7755 @@ -125,7 +124,6 @@ class AnthropicConfig(BaseConfig): is_vertex_request: bool = False, user_anthropic_beta_headers: Optional[List[str]] = None, ) -> dict: - betas = set() if prompt_caching_set: betas.add("prompt-caching-2024-07-31") @@ -143,9 +141,15 @@ class AnthropicConfig(BaseConfig): if user_anthropic_beta_headers is not None: betas.update(user_anthropic_beta_headers) - # Don't send any beta headers to Vertex, Vertex has failed requests when they are sent + # Handle beta headers for Vertex AI + # We allow prompt caching beta header for Vertex, but exclude other beta headers that might cause issues if is_vertex_request is True: - pass + vertex_safe_betas = set() + # Allow prompt caching beta header for Vertex + if "prompt-caching-2024-07-31" in betas: + vertex_safe_betas.add("prompt-caching-2024-07-31") + if len(vertex_safe_betas) > 0: + headers["anthropic-beta"] = ",".join(vertex_safe_betas) elif len(betas) > 0: headers["anthropic-beta"] = ",".join(betas) @@ -300,6 +304,14 @@ class AnthropicConfig(BaseConfig): model: str, drop_params: bool, ) -> dict: + is_thinking_enabled = self.is_thinking_enabled( + non_default_params=non_default_params + ) + + ## handle thinking tokens + self.update_optional_params_with_thinking_tokens( + non_default_params=non_default_params, optional_params=optional_params + ) for param, value in non_default_params.items(): if param == "max_tokens": optional_params["max_tokens"] = value @@ -312,11 +324,11 @@ class AnthropicConfig(BaseConfig): optional_params=optional_params, tools=tool_value ) if param == "tool_choice" or param == "parallel_tool_calls": - _tool_choice: Optional[AnthropicMessagesToolChoice] = ( - self._map_tool_choice( - tool_choice=non_default_params.get("tool_choice"), - parallel_tool_use=non_default_params.get("parallel_tool_calls"), - ) + _tool_choice: Optional[ + AnthropicMessagesToolChoice + ] = self._map_tool_choice( + tool_choice=non_default_params.get("tool_choice"), + parallel_tool_use=non_default_params.get("parallel_tool_calls"), ) if _tool_choice is not None: @@ -332,7 +344,6 @@ class AnthropicConfig(BaseConfig): if param == "top_p": optional_params["top_p"] = value if param == "response_format" and isinstance(value, dict): - ignore_response_format_types = ["text"] if value["type"] in ignore_response_format_types: # value is a no-op continue @@ -349,19 +360,23 @@ class AnthropicConfig(BaseConfig): - Remember that the model will pass the input to the tool, so the name of the tool and description should be from the model’s perspective. """ - _tool_choice = {"name": RESPONSE_FORMAT_TOOL_NAME, "type": "tool"} + if not is_thinking_enabled: + _tool_choice = {"name": RESPONSE_FORMAT_TOOL_NAME, "type": "tool"} + optional_params["tool_choice"] = _tool_choice + _tool = self._create_json_tool_call_for_response_format( json_schema=json_schema, ) optional_params = self._add_tools_to_optional_params( optional_params=optional_params, tools=[_tool] ) - optional_params["tool_choice"] = _tool_choice + optional_params["json_mode"] = True if param == "user": optional_params["metadata"] = {"user_id": value} if param == "thinking": optional_params["thinking"] = value + return optional_params def _create_json_tool_call_for_response_format( @@ -457,9 +472,9 @@ class AnthropicConfig(BaseConfig): text=system_message_block["content"], ) if "cache_control" in system_message_block: - anthropic_system_message_content["cache_control"] = ( - system_message_block["cache_control"] - ) + anthropic_system_message_content[ + "cache_control" + ] = system_message_block["cache_control"] anthropic_system_message_list.append( anthropic_system_message_content ) @@ -473,9 +488,9 @@ class AnthropicConfig(BaseConfig): ) ) if "cache_control" in _content: - anthropic_system_message_content["cache_control"] = ( - _content["cache_control"] - ) + anthropic_system_message_content[ + "cache_control" + ] = _content["cache_control"] anthropic_system_message_list.append( anthropic_system_message_content @@ -584,7 +599,9 @@ class AnthropicConfig(BaseConfig): ) return _message - def extract_response_content(self, completion_response: dict) -> Tuple[ + def extract_response_content( + self, completion_response: dict + ) -> Tuple[ str, Optional[List[Any]], Optional[List[ChatCompletionThinkingBlock]], @@ -680,9 +697,13 @@ class AnthropicConfig(BaseConfig): reasoning_content: Optional[str] = None tool_calls: List[ChatCompletionToolCallChunk] = [] - text_content, citations, thinking_blocks, reasoning_content, tool_calls = ( - self.extract_response_content(completion_response=completion_response) - ) + ( + text_content, + citations, + thinking_blocks, + reasoning_content, + tool_calls, + ) = self.extract_response_content(completion_response=completion_response) _message = litellm.Message( tool_calls=tool_calls, diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index 409bbe2d82..9eae6734ff 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -2,11 +2,14 @@ This file contains common utils for anthropic calls. """ -from typing import Optional, Union +from typing import List, Optional, Union import httpx +import litellm +from litellm.llms.base_llm.base_utils import BaseLLMModelInfo from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.secret_managers.main import get_secret_str class AnthropicError(BaseLLMException): @@ -19,6 +22,54 @@ class AnthropicError(BaseLLMException): super().__init__(status_code=status_code, message=message, headers=headers) +class AnthropicModelInfo(BaseLLMModelInfo): + @staticmethod + def get_api_base(api_base: Optional[str] = None) -> Optional[str]: + return ( + api_base + or get_secret_str("ANTHROPIC_API_BASE") + or "https://api.anthropic.com" + ) + + @staticmethod + def get_api_key(api_key: Optional[str] = None) -> Optional[str]: + return api_key or get_secret_str("ANTHROPIC_API_KEY") + + @staticmethod + def get_base_model(model: Optional[str] = None) -> Optional[str]: + return model.replace("anthropic/", "") if model else None + + def get_models( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> List[str]: + api_base = AnthropicModelInfo.get_api_base(api_base) + api_key = AnthropicModelInfo.get_api_key(api_key) + if api_base is None or api_key is None: + raise ValueError( + "ANTHROPIC_API_BASE or ANTHROPIC_API_KEY is not set. Please set the environment variable, to query Anthropic's `/models` endpoint." + ) + response = litellm.module_level_client.get( + url=f"{api_base}/v1/models", + headers={"x-api-key": api_key, "anthropic-version": "2023-06-01"}, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError: + raise Exception( + f"Failed to fetch models from Anthropic. Status code: {response.status_code}, Response: {response.text}" + ) + + models = response.json()["data"] + + litellm_model_names = [] + for model in models: + stripped_model_name = model["id"] + litellm_model_name = "anthropic/" + stripped_model_name + litellm_model_names.append(litellm_model_name) + return litellm_model_names + + def process_anthropic_headers(headers: Union[httpx.Headers, dict]) -> dict: openai_headers = {} if "anthropic-ratelimit-requests-limit" in headers: diff --git a/litellm/llms/anthropic/completion/transformation.py b/litellm/llms/anthropic/completion/transformation.py index 7a260b6f94..5cbc0b5fd8 100644 --- a/litellm/llms/anthropic/completion/transformation.py +++ b/litellm/llms/anthropic/completion/transformation.py @@ -54,9 +54,9 @@ class AnthropicTextConfig(BaseConfig): to pass metadata to anthropic, it's {"user_id": "any-relevant-information"} """ - max_tokens_to_sample: Optional[int] = ( - litellm.max_tokens - ) # anthropic requires a default + max_tokens_to_sample: Optional[ + int + ] = litellm.max_tokens # anthropic requires a default stop_sequences: Optional[list] = None temperature: Optional[int] = None top_p: Optional[int] = None diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py index 8b4c947a7c..a37d816770 100644 --- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py +++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py @@ -28,7 +28,6 @@ from litellm.utils import ProviderConfigManager, client class AnthropicMessagesHandler: - @staticmethod async def _handle_anthropic_streaming( response: httpx.Response, @@ -88,19 +87,22 @@ async def anthropic_messages( """ # Use provided client or create a new one optional_params = GenericLiteLLMParams(**kwargs) - model, _custom_llm_provider, dynamic_api_key, dynamic_api_base = ( - litellm.get_llm_provider( - model=model, - custom_llm_provider=custom_llm_provider, - api_base=optional_params.api_base, - api_key=optional_params.api_key, - ) + ( + model, + _custom_llm_provider, + dynamic_api_key, + dynamic_api_base, + ) = litellm.get_llm_provider( + model=model, + custom_llm_provider=custom_llm_provider, + api_base=optional_params.api_base, + api_key=optional_params.api_key, ) - anthropic_messages_provider_config: Optional[BaseAnthropicMessagesConfig] = ( - ProviderConfigManager.get_provider_anthropic_messages_config( - model=model, - provider=litellm.LlmProviders(_custom_llm_provider), - ) + anthropic_messages_provider_config: Optional[ + BaseAnthropicMessagesConfig + ] = ProviderConfigManager.get_provider_anthropic_messages_config( + model=model, + provider=litellm.LlmProviders(_custom_llm_provider), ) if anthropic_messages_provider_config is None: raise ValueError( diff --git a/litellm/llms/azure/azure.py b/litellm/llms/azure/azure.py index 03c5cc09eb..aed813fdab 100644 --- a/litellm/llms/azure/azure.py +++ b/litellm/llms/azure/azure.py @@ -654,7 +654,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): ) -> EmbeddingResponse: response = None try: - openai_aclient = self.get_azure_openai_client( api_version=api_version, api_base=api_base, @@ -835,7 +834,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): "2023-10-01-preview", ] ): # CREATE + POLL for azure dall-e-2 calls - api_base = modify_url( original_url=api_base, new_path="/openai/images/generations:submit" ) @@ -867,7 +865,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): ) while response.json()["status"] not in ["succeeded", "failed"]: if time.time() - start_time > timeout_secs: - raise AzureOpenAIError( status_code=408, message="Operation polling timed out." ) @@ -935,7 +932,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): "2023-10-01-preview", ] ): # CREATE + POLL for azure dall-e-2 calls - api_base = modify_url( original_url=api_base, new_path="/openai/images/generations:submit" ) @@ -1199,7 +1195,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): client=None, litellm_params: Optional[dict] = None, ) -> HttpxBinaryResponseContent: - max_retries = optional_params.pop("max_retries", 2) if aspeech is not None and aspeech is True: @@ -1253,7 +1248,6 @@ class AzureChatCompletion(BaseAzureLLM, BaseLLM): client=None, litellm_params: Optional[dict] = None, ) -> HttpxBinaryResponseContent: - azure_client: AsyncAzureOpenAI = self.get_azure_openai_client( api_base=api_base, api_version=api_version, diff --git a/litellm/llms/azure/batches/handler.py b/litellm/llms/azure/batches/handler.py index 1b93c526d5..7fc6388ba8 100644 --- a/litellm/llms/azure/batches/handler.py +++ b/litellm/llms/azure/batches/handler.py @@ -50,15 +50,15 @@ class AzureBatchesAPI(BaseAzureLLM): client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None, litellm_params: Optional[dict] = None, ) -> Union[LiteLLMBatch, Coroutine[Any, Any, LiteLLMBatch]]: - azure_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - litellm_params=litellm_params or {}, - ) + azure_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, + litellm_params=litellm_params or {}, ) if azure_client is None: raise ValueError( @@ -96,15 +96,15 @@ class AzureBatchesAPI(BaseAzureLLM): client: Optional[AzureOpenAI] = None, litellm_params: Optional[dict] = None, ): - azure_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - litellm_params=litellm_params or {}, - ) + azure_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, + litellm_params=litellm_params or {}, ) if azure_client is None: raise ValueError( @@ -144,15 +144,15 @@ class AzureBatchesAPI(BaseAzureLLM): client: Optional[AzureOpenAI] = None, litellm_params: Optional[dict] = None, ): - azure_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - litellm_params=litellm_params or {}, - ) + azure_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, + litellm_params=litellm_params or {}, ) if azure_client is None: raise ValueError( @@ -183,15 +183,15 @@ class AzureBatchesAPI(BaseAzureLLM): client: Optional[AzureOpenAI] = None, litellm_params: Optional[dict] = None, ): - azure_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - litellm_params=litellm_params or {}, - ) + azure_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, + litellm_params=litellm_params or {}, ) if azure_client is None: raise ValueError( diff --git a/litellm/llms/azure/common_utils.py b/litellm/llms/azure/common_utils.py index 71092c8b99..5d61557c21 100644 --- a/litellm/llms/azure/common_utils.py +++ b/litellm/llms/azure/common_utils.py @@ -306,7 +306,6 @@ class BaseAzureLLM(BaseOpenAILLM): api_version: Optional[str], is_async: bool, ) -> dict: - azure_ad_token_provider: Optional[Callable[[], str]] = None # If we have api_key, then we have higher priority azure_ad_token = litellm_params.get("azure_ad_token") diff --git a/litellm/llms/azure/files/handler.py b/litellm/llms/azure/files/handler.py index d45ac9a315..98407d05d5 100644 --- a/litellm/llms/azure/files/handler.py +++ b/litellm/llms/azure/files/handler.py @@ -46,16 +46,15 @@ class AzureOpenAIFilesAPI(BaseAzureLLM): client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None, litellm_params: Optional[dict] = None, ) -> Union[FileObject, Coroutine[Any, Any, FileObject]]: - - openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - litellm_params=litellm_params or {}, - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - ) + openai_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + litellm_params=litellm_params or {}, + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, ) if openai_client is None: raise ValueError( @@ -95,15 +94,15 @@ class AzureOpenAIFilesAPI(BaseAzureLLM): ) -> Union[ HttpxBinaryResponseContent, Coroutine[Any, Any, HttpxBinaryResponseContent] ]: - openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - litellm_params=litellm_params or {}, - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - ) + openai_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + litellm_params=litellm_params or {}, + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, ) if openai_client is None: raise ValueError( @@ -145,15 +144,15 @@ class AzureOpenAIFilesAPI(BaseAzureLLM): client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None, litellm_params: Optional[dict] = None, ): - openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - litellm_params=litellm_params or {}, - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - ) + openai_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + litellm_params=litellm_params or {}, + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, ) if openai_client is None: raise ValueError( @@ -197,15 +196,15 @@ class AzureOpenAIFilesAPI(BaseAzureLLM): client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None, litellm_params: Optional[dict] = None, ): - openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - litellm_params=litellm_params or {}, - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - ) + openai_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + litellm_params=litellm_params or {}, + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, ) if openai_client is None: raise ValueError( @@ -251,15 +250,15 @@ class AzureOpenAIFilesAPI(BaseAzureLLM): client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None, litellm_params: Optional[dict] = None, ): - openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = ( - self.get_azure_openai_client( - litellm_params=litellm_params or {}, - api_key=api_key, - api_base=api_base, - api_version=api_version, - client=client, - _is_async=_is_async, - ) + openai_client: Optional[ + Union[AzureOpenAI, AsyncAzureOpenAI] + ] = self.get_azure_openai_client( + litellm_params=litellm_params or {}, + api_key=api_key, + api_base=api_base, + api_version=api_version, + client=client, + _is_async=_is_async, ) if openai_client is None: raise ValueError( diff --git a/litellm/llms/azure/fine_tuning/handler.py b/litellm/llms/azure/fine_tuning/handler.py index 3d7cc336fb..429b834989 100644 --- a/litellm/llms/azure/fine_tuning/handler.py +++ b/litellm/llms/azure/fine_tuning/handler.py @@ -25,14 +25,7 @@ class AzureOpenAIFineTuningAPI(OpenAIFineTuningAPI, BaseAzureLLM): _is_async: bool = False, api_version: Optional[str] = None, litellm_params: Optional[dict] = None, - ) -> Optional[ - Union[ - OpenAI, - AsyncOpenAI, - AzureOpenAI, - AsyncAzureOpenAI, - ] - ]: + ) -> Optional[Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI,]]: # Override to use Azure-specific client initialization if isinstance(client, OpenAI) or isinstance(client, AsyncOpenAI): client = None diff --git a/litellm/llms/azure_ai/chat/transformation.py b/litellm/llms/azure_ai/chat/transformation.py index 154f345537..a1fd24efa1 100644 --- a/litellm/llms/azure_ai/chat/transformation.py +++ b/litellm/llms/azure_ai/chat/transformation.py @@ -145,7 +145,6 @@ class AzureAIStudioConfig(OpenAIConfig): 2. If message contains an image or audio, send as is (user-intended) """ for message in messages: - # Do nothing if the message contains an image or audio if _audio_or_image_in_message_content(message): continue diff --git a/litellm/llms/azure_ai/embed/cohere_transformation.py b/litellm/llms/azure_ai/embed/cohere_transformation.py index 38b0dbbe23..64433c21b6 100644 --- a/litellm/llms/azure_ai/embed/cohere_transformation.py +++ b/litellm/llms/azure_ai/embed/cohere_transformation.py @@ -22,7 +22,6 @@ class AzureAICohereConfig: pass def _map_azure_model_group(self, model: str) -> str: - if model == "offer-cohere-embed-multili-paygo": return "Cohere-embed-v3-multilingual" elif model == "offer-cohere-embed-english-paygo": diff --git a/litellm/llms/azure_ai/embed/handler.py b/litellm/llms/azure_ai/embed/handler.py index f33c979ca2..da39c5f3b8 100644 --- a/litellm/llms/azure_ai/embed/handler.py +++ b/litellm/llms/azure_ai/embed/handler.py @@ -17,7 +17,6 @@ from .cohere_transformation import AzureAICohereConfig class AzureAIEmbedding(OpenAIChatCompletion): - def _process_response( self, image_embedding_responses: Optional[List], @@ -145,7 +144,6 @@ class AzureAIEmbedding(OpenAIChatCompletion): api_base: Optional[str] = None, client=None, ) -> EmbeddingResponse: - ( image_embeddings_request, v1_embeddings_request, diff --git a/litellm/llms/azure_ai/rerank/transformation.py b/litellm/llms/azure_ai/rerank/transformation.py index 842511f30d..4465e0d70a 100644 --- a/litellm/llms/azure_ai/rerank/transformation.py +++ b/litellm/llms/azure_ai/rerank/transformation.py @@ -17,6 +17,7 @@ class AzureAIRerankConfig(CohereRerankConfig): """ Azure AI Rerank - Follows the same Spec as Cohere Rerank """ + def get_complete_url(self, api_base: Optional[str], model: str) -> str: if api_base is None: raise ValueError( diff --git a/litellm/llms/base.py b/litellm/llms/base.py index deced222ca..abc314bba0 100644 --- a/litellm/llms/base.py +++ b/litellm/llms/base.py @@ -9,7 +9,6 @@ from litellm.types.utils import ModelResponse, TextCompletionResponse class BaseLLM: - _client_session: Optional[httpx.Client] = None def process_response( diff --git a/litellm/llms/base_llm/audio_transcription/transformation.py b/litellm/llms/base_llm/audio_transcription/transformation.py index e550c574e2..d48edb041c 100644 --- a/litellm/llms/base_llm/audio_transcription/transformation.py +++ b/litellm/llms/base_llm/audio_transcription/transformation.py @@ -1,5 +1,5 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional, Union import httpx @@ -8,7 +8,7 @@ from litellm.types.llms.openai import ( AllMessageValues, OpenAIAudioTranscriptionOptionalParams, ) -from litellm.types.utils import ModelResponse +from litellm.types.utils import FileTypes, ModelResponse if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj @@ -42,6 +42,18 @@ class BaseAudioTranscriptionConfig(BaseConfig, ABC): """ return api_base or "" + @abstractmethod + def transform_audio_transcription_request( + self, + model: str, + audio_file: FileTypes, + optional_params: dict, + litellm_params: dict, + ) -> Union[dict, bytes]: + raise NotImplementedError( + "AudioTranscriptionConfig needs a request transformation for audio transcription models" + ) + def transform_request( self, model: str, diff --git a/litellm/llms/base_llm/base_utils.py b/litellm/llms/base_llm/base_utils.py index 919cdbfd02..cef64d01e3 100644 --- a/litellm/llms/base_llm/base_utils.py +++ b/litellm/llms/base_llm/base_utils.py @@ -19,11 +19,19 @@ class BaseLLMModelInfo(ABC): self, model: str, ) -> Optional[ProviderSpecificModelInfo]: + """ + Default values all models of this provider support. + """ return None @abstractmethod - def get_models(self) -> List[str]: - pass + def get_models( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> List[str]: + """ + Returns a list of models supported by this provider. + """ + return [] @staticmethod @abstractmethod diff --git a/litellm/llms/base_llm/chat/transformation.py b/litellm/llms/base_llm/chat/transformation.py index 1b5a6bc58e..b4b120776c 100644 --- a/litellm/llms/base_llm/chat/transformation.py +++ b/litellm/llms/base_llm/chat/transformation.py @@ -13,12 +13,13 @@ from typing import ( Optional, Type, Union, + cast, ) import httpx from pydantic import BaseModel -from litellm.constants import RESPONSE_FORMAT_TOOL_NAME +from litellm.constants import DEFAULT_MAX_TOKENS, RESPONSE_FORMAT_TOOL_NAME from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from litellm.types.llms.openai import ( AllMessageValues, @@ -102,6 +103,29 @@ class BaseConfig(ABC): ) -> Optional[dict]: return type_to_response_format_param(response_format=response_format) + def is_thinking_enabled(self, non_default_params: dict) -> bool: + return non_default_params.get("thinking", {}).get("type", None) == "enabled" + + def update_optional_params_with_thinking_tokens( + self, non_default_params: dict, optional_params: dict + ): + """ + Handles scenario where max tokens is not specified. For anthropic models (anthropic api/bedrock/vertex ai), this requires having the max tokens being set and being greater than the thinking token budget. + + Checks 'non_default_params' for 'thinking' and 'max_tokens' + + if 'thinking' is enabled and 'max_tokens' is not specified, set 'max_tokens' to the thinking token budget + DEFAULT_MAX_TOKENS + """ + is_thinking_enabled = self.is_thinking_enabled(non_default_params) + if is_thinking_enabled and "max_tokens" not in non_default_params: + thinking_token_budget = cast(dict, non_default_params["thinking"]).get( + "budget_tokens", None + ) + if thinking_token_budget is not None: + optional_params["max_tokens"] = ( + thinking_token_budget + DEFAULT_MAX_TOKENS + ) + def should_fake_stream( self, model: Optional[str], @@ -194,7 +218,6 @@ class BaseConfig(ABC): json_schema = value["json_schema"]["schema"] if json_schema and not is_response_format_supported: - _tool_choice = ChatCompletionToolChoiceObjectParam( type="function", function=ChatCompletionToolChoiceFunctionParam( diff --git a/litellm/llms/base_llm/responses/transformation.py b/litellm/llms/base_llm/responses/transformation.py index 29555c55da..e98a579845 100644 --- a/litellm/llms/base_llm/responses/transformation.py +++ b/litellm/llms/base_llm/responses/transformation.py @@ -58,7 +58,6 @@ class BaseResponsesAPIConfig(ABC): model: str, drop_params: bool, ) -> Dict: - pass @abstractmethod diff --git a/litellm/llms/bedrock/chat/converse_handler.py b/litellm/llms/bedrock/chat/converse_handler.py index a4230177b5..7f529c637a 100644 --- a/litellm/llms/bedrock/chat/converse_handler.py +++ b/litellm/llms/bedrock/chat/converse_handler.py @@ -81,7 +81,6 @@ def make_sync_call( class BedrockConverseLLM(BaseAWSLLM): - def __init__(self) -> None: super().__init__() @@ -114,7 +113,6 @@ class BedrockConverseLLM(BaseAWSLLM): fake_stream: bool = False, json_mode: Optional[bool] = False, ) -> CustomStreamWrapper: - request_data = await litellm.AmazonConverseConfig()._async_transform_request( model=model, messages=messages, @@ -179,7 +177,6 @@ class BedrockConverseLLM(BaseAWSLLM): headers: dict = {}, client: Optional[AsyncHTTPHandler] = None, ) -> Union[ModelResponse, CustomStreamWrapper]: - request_data = await litellm.AmazonConverseConfig()._async_transform_request( model=model, messages=messages, @@ -265,7 +262,6 @@ class BedrockConverseLLM(BaseAWSLLM): extra_headers: Optional[dict] = None, client: Optional[Union[AsyncHTTPHandler, HTTPHandler]] = None, ): - ## SETUP ## stream = optional_params.pop("stream", None) unencoded_model_id = optional_params.pop("model_id", None) @@ -301,9 +297,9 @@ class BedrockConverseLLM(BaseAWSLLM): aws_sts_endpoint = optional_params.pop("aws_sts_endpoint", None) optional_params.pop("aws_region_name", None) - litellm_params["aws_region_name"] = ( - aws_region_name # [DO NOT DELETE] important for async calls - ) + litellm_params[ + "aws_region_name" + ] = aws_region_name # [DO NOT DELETE] important for async calls credentials: Credentials = self.get_credentials( aws_access_key_id=aws_access_key_id, diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index bb874cfe38..05386c62b5 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -66,6 +66,13 @@ class AmazonConverseConfig(BaseConfig): def custom_llm_provider(self) -> Optional[str]: return "bedrock_converse" + @classmethod + def get_config_blocks(cls) -> dict: + return { + "guardrailConfig": GuardrailConfigBlock, + "performanceConfig": PerformanceConfigBlock, + } + @classmethod def get_config(cls): return { @@ -210,9 +217,12 @@ class AmazonConverseConfig(BaseConfig): drop_params: bool, messages: Optional[List[AllMessageValues]] = None, ) -> dict: + is_thinking_enabled = self.is_thinking_enabled(non_default_params) + self.update_optional_params_with_thinking_tokens( + non_default_params=non_default_params, optional_params=optional_params + ) for param, value in non_default_params.items(): if param == "response_format" and isinstance(value, dict): - ignore_response_format_types = ["text"] if value["type"] in ignore_response_format_types: # value is a no-op continue @@ -247,8 +257,11 @@ class AmazonConverseConfig(BaseConfig): optional_params = self._add_tools_to_optional_params( optional_params=optional_params, tools=[_tool] ) - if litellm.utils.supports_tool_choice( - model=model, custom_llm_provider=self.custom_llm_provider + if ( + litellm.utils.supports_tool_choice( + model=model, custom_llm_provider=self.custom_llm_provider + ) + and not is_thinking_enabled ): optional_params["tool_choice"] = ToolChoiceValuesBlock( tool=SpecificToolChoiceBlock( @@ -284,6 +297,7 @@ class AmazonConverseConfig(BaseConfig): optional_params["tool_choice"] = _tool_choice_value if param == "thinking": optional_params["thinking"] = value + return optional_params @overload @@ -386,7 +400,6 @@ class AmazonConverseConfig(BaseConfig): optional_params: dict, messages: Optional[List[AllMessageValues]] = None, ) -> CommonRequestObject: - ## VALIDATE REQUEST """ Bedrock doesn't support tool calling without `tools=` param specified. @@ -412,11 +425,11 @@ class AmazonConverseConfig(BaseConfig): AmazonConverseConfig.__annotations__.keys() ) + ["top_k"] supported_tool_call_params = ["tools", "tool_choice"] - supported_guardrail_params = ["guardrailConfig"] + supported_config_params = list(self.get_config_blocks().keys()) total_supported_params = ( supported_converse_params + supported_tool_call_params - + supported_guardrail_params + + supported_config_params ) inference_params.pop("json_mode", None) # used for handling json_schema @@ -455,12 +468,11 @@ class AmazonConverseConfig(BaseConfig): ), } - # Guardrail Config - guardrail_config: Optional[GuardrailConfigBlock] = None - request_guardrails_config = inference_params.pop("guardrailConfig", None) - if request_guardrails_config is not None: - guardrail_config = GuardrailConfigBlock(**request_guardrails_config) - data["guardrailConfig"] = guardrail_config + # Handle all config blocks + for config_name, config_class in self.get_config_blocks().items(): + config_value = inference_params.pop(config_name, None) + if config_value is not None: + data[config_name] = config_class(**config_value) # type: ignore # Tool Config if bedrock_tool_config is not None: @@ -702,9 +714,9 @@ class AmazonConverseConfig(BaseConfig): chat_completion_message: ChatCompletionResponseMessage = {"role": "assistant"} content_str = "" tools: List[ChatCompletionToolCallChunk] = [] - reasoningContentBlocks: Optional[List[BedrockConverseReasoningContentBlock]] = ( - None - ) + reasoningContentBlocks: Optional[ + List[BedrockConverseReasoningContentBlock] + ] = None if message is not None: for idx, content in enumerate(message["content"]): @@ -714,7 +726,6 @@ class AmazonConverseConfig(BaseConfig): if "text" in content: content_str += content["text"] if "toolUse" in content: - ## check tool name was formatted by litellm _response_tool_name = content["toolUse"]["name"] response_tool_name = get_bedrock_tool_name( @@ -741,12 +752,12 @@ class AmazonConverseConfig(BaseConfig): chat_completion_message["provider_specific_fields"] = { "reasoningContentBlocks": reasoningContentBlocks, } - chat_completion_message["reasoning_content"] = ( - self._transform_reasoning_content(reasoningContentBlocks) - ) - chat_completion_message["thinking_blocks"] = ( - self._transform_thinking_blocks(reasoningContentBlocks) - ) + chat_completion_message[ + "reasoning_content" + ] = self._transform_reasoning_content(reasoningContentBlocks) + chat_completion_message[ + "thinking_blocks" + ] = self._transform_thinking_blocks(reasoningContentBlocks) chat_completion_message["content"] = content_str if json_mode is True and tools is not None and len(tools) == 1: # to support 'json_schema' logic on bedrock models diff --git a/litellm/llms/bedrock/chat/invoke_handler.py b/litellm/llms/bedrock/chat/invoke_handler.py index 5b02fd3158..09bdd63572 100644 --- a/litellm/llms/bedrock/chat/invoke_handler.py +++ b/litellm/llms/bedrock/chat/invoke_handler.py @@ -496,9 +496,9 @@ class BedrockLLM(BaseAWSLLM): content=None, ) model_response.choices[0].message = _message # type: ignore - model_response._hidden_params["original_response"] = ( - outputText # allow user to access raw anthropic tool calling response - ) + model_response._hidden_params[ + "original_response" + ] = outputText # allow user to access raw anthropic tool calling response if ( _is_function_call is True and stream is not None @@ -806,9 +806,9 @@ class BedrockLLM(BaseAWSLLM): ): # completion(top_k=3) > anthropic_config(top_k=3) <- allows for dynamic variables to be passed in inference_params[k] = v if stream is True: - inference_params["stream"] = ( - True # cohere requires stream = True in inference params - ) + inference_params[ + "stream" + ] = True # cohere requires stream = True in inference params data = json.dumps({"prompt": prompt, **inference_params}) elif provider == "anthropic": if model.startswith("anthropic.claude-3"): @@ -1205,7 +1205,6 @@ class BedrockLLM(BaseAWSLLM): def get_response_stream_shape(): global _response_stream_shape_cache if _response_stream_shape_cache is None: - from botocore.loaders import Loader from botocore.model import ServiceModel @@ -1539,7 +1538,6 @@ class AmazonDeepSeekR1StreamDecoder(AWSEventStreamDecoder): model: str, sync_stream: bool, ) -> None: - super().__init__(model=model) from litellm.llms.bedrock.chat.invoke_transformations.amazon_deepseek_transformation import ( AmazonDeepseekR1ResponseIterator, diff --git a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py index 133eb659df..d1212705d8 100644 --- a/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py +++ b/litellm/llms/bedrock/chat/invoke_transformations/base_invoke_transformation.py @@ -225,9 +225,9 @@ class AmazonInvokeConfig(BaseConfig, BaseAWSLLM): ): # completion(top_k=3) > anthropic_config(top_k=3) <- allows for dynamic variables to be passed in inference_params[k] = v if stream is True: - inference_params["stream"] = ( - True # cohere requires stream = True in inference params - ) + inference_params[ + "stream" + ] = True # cohere requires stream = True in inference params request_data = {"prompt": prompt, **inference_params} elif provider == "anthropic": return litellm.AmazonAnthropicClaude3Config().transform_request( @@ -311,7 +311,6 @@ class AmazonInvokeConfig(BaseConfig, BaseAWSLLM): api_key: Optional[str] = None, json_mode: Optional[bool] = None, ) -> ModelResponse: - try: completion_response = raw_response.json() except Exception: diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py index 4677a579ed..f4a1170660 100644 --- a/litellm/llms/bedrock/common_utils.py +++ b/litellm/llms/bedrock/common_utils.py @@ -314,7 +314,6 @@ def get_bedrock_tool_name(response_tool_name: str) -> str: class BedrockModelInfo(BaseLLMModelInfo): - global_config = AmazonBedrockGlobalConfig() all_global_regions = global_config.get_all_regions() diff --git a/litellm/llms/bedrock/embed/amazon_titan_multimodal_transformation.py b/litellm/llms/bedrock/embed/amazon_titan_multimodal_transformation.py index 6c1147f24a..338029adc3 100644 --- a/litellm/llms/bedrock/embed/amazon_titan_multimodal_transformation.py +++ b/litellm/llms/bedrock/embed/amazon_titan_multimodal_transformation.py @@ -33,9 +33,9 @@ class AmazonTitanMultimodalEmbeddingG1Config: ) -> dict: for k, v in non_default_params.items(): if k == "dimensions": - optional_params["embeddingConfig"] = ( - AmazonTitanMultimodalEmbeddingConfig(outputEmbeddingLength=v) - ) + optional_params[ + "embeddingConfig" + ] = AmazonTitanMultimodalEmbeddingConfig(outputEmbeddingLength=v) return optional_params def _transform_request( @@ -58,7 +58,6 @@ class AmazonTitanMultimodalEmbeddingG1Config: def _transform_response( self, response_list: List[dict], model: str ) -> EmbeddingResponse: - total_prompt_tokens = 0 transformed_responses: List[Embedding] = [] for index, response in enumerate(response_list): diff --git a/litellm/llms/bedrock/image/amazon_nova_canvas_transformation.py b/litellm/llms/bedrock/image/amazon_nova_canvas_transformation.py index 33249f9af8..b331dd1b1d 100644 --- a/litellm/llms/bedrock/image/amazon_nova_canvas_transformation.py +++ b/litellm/llms/bedrock/image/amazon_nova_canvas_transformation.py @@ -1,12 +1,16 @@ import types -from typing import List, Optional +from typing import Any, Dict, List, Optional from openai.types.image import Image from litellm.types.llms.bedrock import ( - AmazonNovaCanvasTextToImageRequest, AmazonNovaCanvasTextToImageResponse, - AmazonNovaCanvasTextToImageParams, AmazonNovaCanvasRequestBase, AmazonNovaCanvasColorGuidedGenerationParams, + AmazonNovaCanvasColorGuidedGenerationParams, AmazonNovaCanvasColorGuidedRequest, + AmazonNovaCanvasImageGenerationConfig, + AmazonNovaCanvasRequestBase, + AmazonNovaCanvasTextToImageParams, + AmazonNovaCanvasTextToImageRequest, + AmazonNovaCanvasTextToImageResponse, ) from litellm.types.utils import ImageResponse @@ -23,7 +27,7 @@ class AmazonNovaCanvasConfig: k: v for k, v in cls.__dict__.items() if not k.startswith("__") - and not isinstance( + and not isinstance( v, ( types.FunctionType, @@ -32,13 +36,12 @@ class AmazonNovaCanvasConfig: staticmethod, ), ) - and v is not None + and v is not None } @classmethod def get_supported_openai_params(cls, model: Optional[str] = None) -> List: - """ - """ + """ """ return ["n", "size", "quality"] @classmethod @@ -56,7 +59,7 @@ class AmazonNovaCanvasConfig: @classmethod def transform_request_body( - cls, text: str, optional_params: dict + cls, text: str, optional_params: dict ) -> AmazonNovaCanvasRequestBase: """ Transform the request body for Amazon Nova Canvas model @@ -65,18 +68,64 @@ class AmazonNovaCanvasConfig: image_generation_config = optional_params.pop("imageGenerationConfig", {}) image_generation_config = {**image_generation_config, **optional_params} if task_type == "TEXT_IMAGE": - text_to_image_params = image_generation_config.pop("textToImageParams", {}) - text_to_image_params = {"text" :text, **text_to_image_params} - text_to_image_params = AmazonNovaCanvasTextToImageParams(**text_to_image_params) - return AmazonNovaCanvasTextToImageRequest(textToImageParams=text_to_image_params, taskType=task_type, - imageGenerationConfig=image_generation_config) + text_to_image_params: Dict[str, Any] = image_generation_config.pop( + "textToImageParams", {} + ) + text_to_image_params = {"text": text, **text_to_image_params} + try: + text_to_image_params_typed = AmazonNovaCanvasTextToImageParams( + **text_to_image_params # type: ignore + ) + except Exception as e: + raise ValueError( + f"Error transforming text to image params: {e}. Got params: {text_to_image_params}, Expected params: {AmazonNovaCanvasTextToImageParams.__annotations__}" + ) + + try: + image_generation_config_typed = AmazonNovaCanvasImageGenerationConfig( + **image_generation_config + ) + except Exception as e: + raise ValueError( + f"Error transforming image generation config: {e}. Got params: {image_generation_config}, Expected params: {AmazonNovaCanvasImageGenerationConfig.__annotations__}" + ) + + return AmazonNovaCanvasTextToImageRequest( + textToImageParams=text_to_image_params_typed, + taskType=task_type, + imageGenerationConfig=image_generation_config_typed, + ) if task_type == "COLOR_GUIDED_GENERATION": - color_guided_generation_params = image_generation_config.pop("colorGuidedGenerationParams", {}) - color_guided_generation_params = {"text": text, **color_guided_generation_params} - color_guided_generation_params = AmazonNovaCanvasColorGuidedGenerationParams(**color_guided_generation_params) - return AmazonNovaCanvasColorGuidedRequest(taskType=task_type, - colorGuidedGenerationParams=color_guided_generation_params, - imageGenerationConfig=image_generation_config) + color_guided_generation_params: Dict[ + str, Any + ] = image_generation_config.pop("colorGuidedGenerationParams", {}) + color_guided_generation_params = { + "text": text, + **color_guided_generation_params, + } + try: + color_guided_generation_params_typed = AmazonNovaCanvasColorGuidedGenerationParams( + **color_guided_generation_params # type: ignore + ) + except Exception as e: + raise ValueError( + f"Error transforming color guided generation params: {e}. Got params: {color_guided_generation_params}, Expected params: {AmazonNovaCanvasColorGuidedGenerationParams.__annotations__}" + ) + + try: + image_generation_config_typed = AmazonNovaCanvasImageGenerationConfig( + **image_generation_config + ) + except Exception as e: + raise ValueError( + f"Error transforming image generation config: {e}. Got params: {image_generation_config}, Expected params: {AmazonNovaCanvasImageGenerationConfig.__annotations__}" + ) + + return AmazonNovaCanvasColorGuidedRequest( + taskType=task_type, + colorGuidedGenerationParams=color_guided_generation_params_typed, + imageGenerationConfig=image_generation_config_typed, + ) raise NotImplementedError(f"Task type {task_type} is not supported") @classmethod @@ -87,7 +136,9 @@ class AmazonNovaCanvasConfig: _size = non_default_params.get("size") if _size is not None: width, height = _size.split("x") - optional_params["width"], optional_params["height"] = int(width), int(height) + optional_params["width"], optional_params["height"] = int(width), int( + height + ) if non_default_params.get("n") is not None: optional_params["numberOfImages"] = non_default_params.get("n") if non_default_params.get("quality") is not None: @@ -99,7 +150,7 @@ class AmazonNovaCanvasConfig: @classmethod def transform_response_dict_to_openai_response( - cls, model_response: ImageResponse, response_dict: dict + cls, model_response: ImageResponse, response_dict: dict ) -> ImageResponse: """ Transform the response dict to the OpenAI response diff --git a/litellm/llms/bedrock/image/image_handler.py b/litellm/llms/bedrock/image/image_handler.py index 8f7762e547..27258aa20f 100644 --- a/litellm/llms/bedrock/image/image_handler.py +++ b/litellm/llms/bedrock/image/image_handler.py @@ -267,7 +267,11 @@ class BedrockImageGeneration(BaseAWSLLM): **inference_params, } elif provider == "amazon": - return dict(litellm.AmazonNovaCanvasConfig.transform_request_body(text=prompt, optional_params=optional_params)) + return dict( + litellm.AmazonNovaCanvasConfig.transform_request_body( + text=prompt, optional_params=optional_params + ) + ) else: raise BedrockError( status_code=422, message=f"Unsupported model={model}, passed in" @@ -303,8 +307,11 @@ class BedrockImageGeneration(BaseAWSLLM): config_class = ( litellm.AmazonStability3Config if litellm.AmazonStability3Config._is_stability_3_model(model=model) - else litellm.AmazonNovaCanvasConfig if litellm.AmazonNovaCanvasConfig._is_nova_model(model=model) - else litellm.AmazonStabilityConfig + else ( + litellm.AmazonNovaCanvasConfig + if litellm.AmazonNovaCanvasConfig._is_nova_model(model=model) + else litellm.AmazonStabilityConfig + ) ) config_class.transform_response_dict_to_openai_response( model_response=model_response, diff --git a/litellm/llms/bedrock/rerank/handler.py b/litellm/llms/bedrock/rerank/handler.py index cd8be6912c..f5a532bec1 100644 --- a/litellm/llms/bedrock/rerank/handler.py +++ b/litellm/llms/bedrock/rerank/handler.py @@ -60,7 +60,6 @@ class BedrockRerankHandler(BaseAWSLLM): extra_headers: Optional[dict] = None, client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, ) -> RerankResponse: - request_data = RerankRequest( model=model, query=query, diff --git a/litellm/llms/bedrock/rerank/transformation.py b/litellm/llms/bedrock/rerank/transformation.py index a5380febe9..be8250a967 100644 --- a/litellm/llms/bedrock/rerank/transformation.py +++ b/litellm/llms/bedrock/rerank/transformation.py @@ -29,7 +29,6 @@ from litellm.types.rerank import ( class BedrockRerankConfig: - def _transform_sources( self, documents: List[Union[str, dict]] ) -> List[BedrockRerankSource]: diff --git a/litellm/llms/codestral/completion/handler.py b/litellm/llms/codestral/completion/handler.py index fc6d2886a9..555f7fccfb 100644 --- a/litellm/llms/codestral/completion/handler.py +++ b/litellm/llms/codestral/completion/handler.py @@ -314,7 +314,6 @@ class CodestralTextCompletion: return _response ### SYNC COMPLETION else: - response = litellm.module_level_client.post( url=completion_url, headers=headers, @@ -352,13 +351,11 @@ class CodestralTextCompletion: logger_fn=None, headers={}, ) -> TextCompletionResponse: - async_handler = get_async_httpx_client( llm_provider=litellm.LlmProviders.TEXT_COMPLETION_CODESTRAL, params={"timeout": timeout}, ) try: - response = await async_handler.post( api_base, headers=headers, data=json.dumps(data) ) diff --git a/litellm/llms/codestral/completion/transformation.py b/litellm/llms/codestral/completion/transformation.py index 5955e91deb..fc7b6f5dbb 100644 --- a/litellm/llms/codestral/completion/transformation.py +++ b/litellm/llms/codestral/completion/transformation.py @@ -78,7 +78,6 @@ class CodestralTextCompletionConfig(OpenAITextCompletionConfig): return optional_params def _chunk_parser(self, chunk_data: str) -> GenericStreamingChunk: - text = "" is_finished = False finish_reason = None diff --git a/litellm/llms/cohere/chat/transformation.py b/litellm/llms/cohere/chat/transformation.py index 3ceec2dbba..fbaedca8f6 100644 --- a/litellm/llms/cohere/chat/transformation.py +++ b/litellm/llms/cohere/chat/transformation.py @@ -180,7 +180,6 @@ class CohereChatConfig(BaseConfig): litellm_params: dict, headers: dict, ) -> dict: - ## Load Config for k, v in litellm.CohereChatConfig.get_config().items(): if ( @@ -222,7 +221,6 @@ class CohereChatConfig(BaseConfig): api_key: Optional[str] = None, json_mode: Optional[bool] = None, ) -> ModelResponse: - try: raw_response_json = raw_response.json() model_response.choices[0].message.content = raw_response_json["text"] # type: ignore diff --git a/litellm/llms/cohere/embed/handler.py b/litellm/llms/cohere/embed/handler.py index e7f22ea72a..7a25bf7e54 100644 --- a/litellm/llms/cohere/embed/handler.py +++ b/litellm/llms/cohere/embed/handler.py @@ -56,7 +56,6 @@ async def async_embedding( encoding: Callable, client: Optional[AsyncHTTPHandler] = None, ): - ## LOGGING logging_obj.pre_call( input=input, diff --git a/litellm/llms/cohere/embed/transformation.py b/litellm/llms/cohere/embed/transformation.py index 22e157a0fd..837dd5e006 100644 --- a/litellm/llms/cohere/embed/transformation.py +++ b/litellm/llms/cohere/embed/transformation.py @@ -72,7 +72,6 @@ class CohereEmbeddingConfig: return transformed_request def _calculate_usage(self, input: List[str], encoding: Any, meta: dict) -> Usage: - input_tokens = 0 text_tokens: Optional[int] = meta.get("billed_units", {}).get("input_tokens") @@ -111,7 +110,6 @@ class CohereEmbeddingConfig: encoding: Any, input: list, ) -> EmbeddingResponse: - response_json = response.json() ## LOGGING logging_obj.post_call( diff --git a/litellm/llms/cohere/rerank/transformation.py b/litellm/llms/cohere/rerank/transformation.py index f3624d9216..22782c1300 100644 --- a/litellm/llms/cohere/rerank/transformation.py +++ b/litellm/llms/cohere/rerank/transformation.py @@ -148,4 +148,4 @@ class CohereRerankConfig(BaseRerankConfig): def get_error_class( self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers] ) -> BaseLLMException: - return CohereError(message=error_message, status_code=status_code) \ No newline at end of file + return CohereError(message=error_message, status_code=status_code) diff --git a/litellm/llms/cohere/rerank_v2/transformation.py b/litellm/llms/cohere/rerank_v2/transformation.py index a93cb982a7..74e760460d 100644 --- a/litellm/llms/cohere/rerank_v2/transformation.py +++ b/litellm/llms/cohere/rerank_v2/transformation.py @@ -3,6 +3,7 @@ from typing import Any, Dict, List, Optional, Union from litellm.llms.cohere.rerank.transformation import CohereRerankConfig from litellm.types.rerank import OptionalRerankParams, RerankRequest + class CohereRerankV2Config(CohereRerankConfig): """ Reference: https://docs.cohere.com/v2/reference/rerank @@ -77,4 +78,4 @@ class CohereRerankV2Config(CohereRerankConfig): return_documents=optional_rerank_params.get("return_documents", None), max_tokens_per_doc=optional_rerank_params.get("max_tokens_per_doc", None), ) - return rerank_request.model_dump(exclude_none=True) \ No newline at end of file + return rerank_request.model_dump(exclude_none=True) diff --git a/litellm/llms/custom_httpx/aiohttp_handler.py b/litellm/llms/custom_httpx/aiohttp_handler.py index c865fee17e..9568ce7185 100644 --- a/litellm/llms/custom_httpx/aiohttp_handler.py +++ b/litellm/llms/custom_httpx/aiohttp_handler.py @@ -32,7 +32,6 @@ DEFAULT_TIMEOUT = 600 class BaseLLMAIOHTTPHandler: - def __init__(self): self.client_session: Optional[aiohttp.ClientSession] = None @@ -110,7 +109,6 @@ class BaseLLMAIOHTTPHandler: content: Any = None, params: Optional[dict] = None, ) -> httpx.Response: - max_retry_on_unprocessable_entity_error = ( provider_config.max_retry_on_unprocessable_entity_error ) diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py index 34d70434d5..23d7fe4b4d 100644 --- a/litellm/llms/custom_httpx/http_handler.py +++ b/litellm/llms/custom_httpx/http_handler.py @@ -114,7 +114,6 @@ class AsyncHTTPHandler: event_hooks: Optional[Mapping[str, List[Callable[..., Any]]]], ssl_verify: Optional[VerifyTypes] = None, ) -> httpx.AsyncClient: - # SSL certificates (a.k.a CA bundle) used to verify the identity of requested hosts. # /path/to/certificate.pem if ssl_verify is None: @@ -590,7 +589,6 @@ class HTTPHandler: timeout: Optional[Union[float, httpx.Timeout]] = None, ): try: - if timeout is not None: req = self.client.build_request( "PATCH", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore @@ -609,7 +607,6 @@ class HTTPHandler: llm_provider="litellm-httpx-handler", ) except httpx.HTTPStatusError as e: - if stream is True: setattr(e, "message", mask_sensitive_info(e.response.read())) setattr(e, "text", mask_sensitive_info(e.response.read())) @@ -635,7 +632,6 @@ class HTTPHandler: timeout: Optional[Union[float, httpx.Timeout]] = None, ): try: - if timeout is not None: req = self.client.build_request( "PUT", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py index 00caf55207..12736640f1 100644 --- a/litellm/llms/custom_httpx/llm_http_handler.py +++ b/litellm/llms/custom_httpx/llm_http_handler.py @@ -1,4 +1,3 @@ -import io import json from typing import TYPE_CHECKING, Any, Coroutine, Dict, Optional, Tuple, Union @@ -8,6 +7,9 @@ import litellm import litellm.litellm_core_utils import litellm.types import litellm.types.utils +from litellm.llms.base_llm.audio_transcription.transformation import ( + BaseAudioTranscriptionConfig, +) from litellm.llms.base_llm.chat.transformation import BaseConfig from litellm.llms.base_llm.embedding.transformation import BaseEmbeddingConfig from litellm.llms.base_llm.rerank.transformation import BaseRerankConfig @@ -39,7 +41,6 @@ else: class BaseLLMHTTPHandler: - async def _make_common_async_call( self, async_httpx_client: AsyncHTTPHandler, @@ -107,7 +108,6 @@ class BaseLLMHTTPHandler: logging_obj: LiteLLMLoggingObj, stream: bool = False, ) -> httpx.Response: - max_retry_on_unprocessable_entity_error = ( provider_config.max_retry_on_unprocessable_entity_error ) @@ -597,7 +597,6 @@ class BaseLLMHTTPHandler: aembedding: bool = False, headers={}, ) -> EmbeddingResponse: - provider_config = ProviderConfigManager.get_provider_embedding_config( model=model, provider=litellm.LlmProviders(custom_llm_provider) ) @@ -740,7 +739,6 @@ class BaseLLMHTTPHandler: api_base: Optional[str] = None, client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, ) -> RerankResponse: - # get config from model, custom llm provider headers = provider_config.validate_environment( api_key=api_key, @@ -826,7 +824,6 @@ class BaseLLMHTTPHandler: timeout: Optional[Union[float, httpx.Timeout]] = None, client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, ) -> RerankResponse: - if client is None or not isinstance(client, AsyncHTTPHandler): async_httpx_client = get_async_httpx_client( llm_provider=litellm.LlmProviders(custom_llm_provider) @@ -852,54 +849,12 @@ class BaseLLMHTTPHandler: request_data=request_data, ) - def handle_audio_file(self, audio_file: FileTypes) -> bytes: - """ - Processes the audio file input based on its type and returns the binary data. - - Args: - audio_file: Can be a file path (str), a tuple (filename, file_content), or binary data (bytes). - - Returns: - The binary data of the audio file. - """ - binary_data: bytes # Explicitly declare the type - - # Handle the audio file based on type - if isinstance(audio_file, str): - # If it's a file path - with open(audio_file, "rb") as f: - binary_data = f.read() # `f.read()` always returns `bytes` - elif isinstance(audio_file, tuple): - # Handle tuple case - _, file_content = audio_file[:2] - if isinstance(file_content, str): - with open(file_content, "rb") as f: - binary_data = f.read() # `f.read()` always returns `bytes` - elif isinstance(file_content, bytes): - binary_data = file_content - else: - raise TypeError( - f"Unexpected type in tuple: {type(file_content)}. Expected str or bytes." - ) - elif isinstance(audio_file, bytes): - # Assume it's already binary data - binary_data = audio_file - elif isinstance(audio_file, io.BufferedReader) or isinstance( - audio_file, io.BytesIO - ): - # Handle file-like objects - binary_data = audio_file.read() - - else: - raise TypeError(f"Unsupported type for audio_file: {type(audio_file)}") - - return binary_data - def audio_transcriptions( self, model: str, audio_file: FileTypes, optional_params: dict, + litellm_params: dict, model_response: TranscriptionResponse, timeout: float, max_retries: int, @@ -910,11 +865,8 @@ class BaseLLMHTTPHandler: client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, atranscription: bool = False, headers: dict = {}, - litellm_params: dict = {}, + provider_config: Optional[BaseAudioTranscriptionConfig] = None, ) -> TranscriptionResponse: - provider_config = ProviderConfigManager.get_provider_audio_transcription_config( - model=model, provider=litellm.LlmProviders(custom_llm_provider) - ) if provider_config is None: raise ValueError( f"No provider config found for model: {model} and provider: {custom_llm_provider}" @@ -938,7 +890,18 @@ class BaseLLMHTTPHandler: ) # Handle the audio file based on type - binary_data = self.handle_audio_file(audio_file) + data = provider_config.transform_audio_transcription_request( + model=model, + audio_file=audio_file, + optional_params=optional_params, + litellm_params=litellm_params, + ) + binary_data: Optional[bytes] = None + json_data: Optional[dict] = None + if isinstance(data, bytes): + binary_data = data + else: + json_data = data try: # Make the POST request @@ -946,6 +909,7 @@ class BaseLLMHTTPHandler: url=complete_url, headers=headers, content=binary_data, + json=json_data, timeout=timeout, ) except Exception as e: diff --git a/litellm/llms/databricks/common_utils.py b/litellm/llms/databricks/common_utils.py index e8481e25b2..76bd281d4d 100644 --- a/litellm/llms/databricks/common_utils.py +++ b/litellm/llms/databricks/common_utils.py @@ -16,9 +16,9 @@ class DatabricksBase: api_base = api_base or f"{databricks_client.config.host}/serving-endpoints" if api_key is None: - databricks_auth_headers: dict[str, str] = ( - databricks_client.config.authenticate() - ) + databricks_auth_headers: dict[ + str, str + ] = databricks_client.config.authenticate() headers = {**databricks_auth_headers, **headers} return api_base, headers diff --git a/litellm/llms/databricks/embed/transformation.py b/litellm/llms/databricks/embed/transformation.py index 53e3b30dd2..a113a349cc 100644 --- a/litellm/llms/databricks/embed/transformation.py +++ b/litellm/llms/databricks/embed/transformation.py @@ -11,9 +11,9 @@ class DatabricksEmbeddingConfig: Reference: https://learn.microsoft.com/en-us/azure/databricks/machine-learning/foundation-models/api-reference#--embedding-task """ - instruction: Optional[str] = ( - None # An optional instruction to pass to the embedding model. BGE Authors recommend 'Represent this sentence for searching relevant passages:' for retrieval queries - ) + instruction: Optional[ + str + ] = None # An optional instruction to pass to the embedding model. BGE Authors recommend 'Represent this sentence for searching relevant passages:' for retrieval queries def __init__(self, instruction: Optional[str] = None) -> None: locals_ = locals().copy() diff --git a/litellm/llms/databricks/streaming_utils.py b/litellm/llms/databricks/streaming_utils.py index 2db53df908..eebe318288 100644 --- a/litellm/llms/databricks/streaming_utils.py +++ b/litellm/llms/databricks/streaming_utils.py @@ -55,7 +55,6 @@ class ModelResponseIterator: usage_chunk: Optional[Usage] = getattr(processed_chunk, "usage", None) if usage_chunk is not None: - usage = ChatCompletionUsageBlock( prompt_tokens=usage_chunk.prompt_tokens, completion_tokens=usage_chunk.completion_tokens, diff --git a/litellm/llms/deepgram/audio_transcription/transformation.py b/litellm/llms/deepgram/audio_transcription/transformation.py index 06296736ea..20599e3994 100644 --- a/litellm/llms/deepgram/audio_transcription/transformation.py +++ b/litellm/llms/deepgram/audio_transcription/transformation.py @@ -2,6 +2,7 @@ Translates from OpenAI's `/v1/audio/transcriptions` to Deepgram's `/v1/listen` """ +import io from typing import List, Optional, Union from httpx import Headers, Response @@ -12,7 +13,7 @@ from litellm.types.llms.openai import ( AllMessageValues, OpenAIAudioTranscriptionOptionalParams, ) -from litellm.types.utils import TranscriptionResponse +from litellm.types.utils import FileTypes, TranscriptionResponse from ...base_llm.audio_transcription.transformation import ( BaseAudioTranscriptionConfig, @@ -47,6 +48,55 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig): message=error_message, status_code=status_code, headers=headers ) + def transform_audio_transcription_request( + self, + model: str, + audio_file: FileTypes, + optional_params: dict, + litellm_params: dict, + ) -> Union[dict, bytes]: + """ + Processes the audio file input based on its type and returns the binary data. + + Args: + audio_file: Can be a file path (str), a tuple (filename, file_content), or binary data (bytes). + + Returns: + The binary data of the audio file. + """ + binary_data: bytes # Explicitly declare the type + + # Handle the audio file based on type + if isinstance(audio_file, str): + # If it's a file path + with open(audio_file, "rb") as f: + binary_data = f.read() # `f.read()` always returns `bytes` + elif isinstance(audio_file, tuple): + # Handle tuple case + _, file_content = audio_file[:2] + if isinstance(file_content, str): + with open(file_content, "rb") as f: + binary_data = f.read() # `f.read()` always returns `bytes` + elif isinstance(file_content, bytes): + binary_data = file_content + else: + raise TypeError( + f"Unexpected type in tuple: {type(file_content)}. Expected str or bytes." + ) + elif isinstance(audio_file, bytes): + # Assume it's already binary data + binary_data = audio_file + elif isinstance(audio_file, io.BufferedReader) or isinstance( + audio_file, io.BytesIO + ): + # Handle file-like objects + binary_data = audio_file.read() + + else: + raise TypeError(f"Unsupported type for audio_file: {type(audio_file)}") + + return binary_data + def transform_audio_transcription_response( self, model: str, @@ -76,9 +126,9 @@ class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig): # Add additional metadata matching OpenAI format response["task"] = "transcribe" - response["language"] = ( - "english" # Deepgram auto-detects but doesn't return language - ) + response[ + "language" + ] = "english" # Deepgram auto-detects but doesn't return language response["duration"] = response_json["metadata"]["duration"] # Transform words to match OpenAI format diff --git a/litellm/llms/deepseek/chat/transformation.py b/litellm/llms/deepseek/chat/transformation.py index 180cf7dc69..fe70ebe77e 100644 --- a/litellm/llms/deepseek/chat/transformation.py +++ b/litellm/llms/deepseek/chat/transformation.py @@ -14,7 +14,6 @@ from ...openai.chat.gpt_transformation import OpenAIGPTConfig class DeepSeekChatConfig(OpenAIGPTConfig): - def _transform_messages( self, messages: List[AllMessageValues], model: str ) -> List[AllMessageValues]: diff --git a/litellm/llms/deprecated_providers/aleph_alpha.py b/litellm/llms/deprecated_providers/aleph_alpha.py index 81ad134641..4cfede2a1b 100644 --- a/litellm/llms/deprecated_providers/aleph_alpha.py +++ b/litellm/llms/deprecated_providers/aleph_alpha.py @@ -77,9 +77,9 @@ class AlephAlphaConfig: - `control_log_additive` (boolean; default value: true): Method of applying control to attention scores. """ - maximum_tokens: Optional[int] = ( - litellm.max_tokens - ) # aleph alpha requires max tokens + maximum_tokens: Optional[ + int + ] = litellm.max_tokens # aleph alpha requires max tokens minimum_tokens: Optional[int] = None echo: Optional[bool] = None temperature: Optional[int] = None diff --git a/litellm/llms/fireworks_ai/audio_transcription/transformation.py b/litellm/llms/fireworks_ai/audio_transcription/transformation.py index 8f35705299..00bb5f2679 100644 --- a/litellm/llms/fireworks_ai/audio_transcription/transformation.py +++ b/litellm/llms/fireworks_ai/audio_transcription/transformation.py @@ -2,27 +2,16 @@ from typing import List from litellm.types.llms.openai import OpenAIAudioTranscriptionOptionalParams -from ...base_llm.audio_transcription.transformation import BaseAudioTranscriptionConfig +from ...openai.transcriptions.whisper_transformation import ( + OpenAIWhisperAudioTranscriptionConfig, +) from ..common_utils import FireworksAIMixin class FireworksAIAudioTranscriptionConfig( - FireworksAIMixin, BaseAudioTranscriptionConfig + FireworksAIMixin, OpenAIWhisperAudioTranscriptionConfig ): def get_supported_openai_params( self, model: str ) -> List[OpenAIAudioTranscriptionOptionalParams]: return ["language", "prompt", "response_format", "timestamp_granularities"] - - def map_openai_params( - self, - non_default_params: dict, - optional_params: dict, - model: str, - drop_params: bool, - ) -> dict: - supported_params = self.get_supported_openai_params(model) - for k, v in non_default_params.items(): - if k in supported_params: - optional_params[k] = v - return optional_params diff --git a/litellm/llms/fireworks_ai/chat/transformation.py b/litellm/llms/fireworks_ai/chat/transformation.py index 1c82f24ac0..4def12adb7 100644 --- a/litellm/llms/fireworks_ai/chat/transformation.py +++ b/litellm/llms/fireworks_ai/chat/transformation.py @@ -88,7 +88,6 @@ class FireworksAIConfig(OpenAIGPTConfig): model: str, drop_params: bool, ) -> dict: - supported_openai_params = self.get_supported_openai_params(model=model) is_tools_set = any( param == "tools" and value is not None @@ -104,7 +103,6 @@ class FireworksAIConfig(OpenAIGPTConfig): # pass through the value of tool choice optional_params["tool_choice"] = value elif param == "response_format": - if ( is_tools_set ): # fireworks ai doesn't support tools and response_format together @@ -223,7 +221,6 @@ class FireworksAIConfig(OpenAIGPTConfig): return api_base, dynamic_api_key def get_models(self, api_key: Optional[str] = None, api_base: Optional[str] = None): - api_base, api_key = self._get_openai_compatible_provider_info( api_base=api_base, api_key=api_key ) diff --git a/litellm/llms/gemini/chat/transformation.py b/litellm/llms/gemini/chat/transformation.py index fbc1916dcc..0d5956122e 100644 --- a/litellm/llms/gemini/chat/transformation.py +++ b/litellm/llms/gemini/chat/transformation.py @@ -90,7 +90,6 @@ class GoogleAIStudioGeminiConfig(VertexGeminiConfig): model: str, drop_params: bool, ) -> Dict: - if litellm.vertex_ai_safety_settings is not None: optional_params["safety_settings"] = litellm.vertex_ai_safety_settings return super().map_openai_params( diff --git a/litellm/llms/gemini/common_utils.py b/litellm/llms/gemini/common_utils.py new file mode 100644 index 0000000000..4c3357a500 --- /dev/null +++ b/litellm/llms/gemini/common_utils.py @@ -0,0 +1,51 @@ +from typing import List, Optional + +import litellm +from litellm.llms.base_llm.base_utils import BaseLLMModelInfo +from litellm.secret_managers.main import get_secret_str + + +class GeminiModelInfo(BaseLLMModelInfo): + @staticmethod + def get_api_base(api_base: Optional[str] = None) -> Optional[str]: + return ( + api_base + or get_secret_str("GEMINI_API_BASE") + or "https://generativelanguage.googleapis.com/v1beta" + ) + + @staticmethod + def get_api_key(api_key: Optional[str] = None) -> Optional[str]: + return api_key or (get_secret_str("GEMINI_API_KEY")) + + @staticmethod + def get_base_model(model: str) -> Optional[str]: + return model.replace("gemini/", "") + + def get_models( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> List[str]: + api_base = GeminiModelInfo.get_api_base(api_base) + api_key = GeminiModelInfo.get_api_key(api_key) + if api_base is None or api_key is None: + raise ValueError( + "GEMINI_API_BASE or GEMINI_API_KEY is not set. Please set the environment variable, to query Gemini's `/models` endpoint." + ) + + response = litellm.module_level_client.get( + url=f"{api_base}/models?key={api_key}", + ) + + if response.status_code != 200: + raise ValueError( + f"Failed to fetch models from Gemini. Status code: {response.status_code}, Response: {response.json()}" + ) + + models = response.json()["models"] + + litellm_model_names = [] + for model in models: + stripped_model_name = model["name"].strip("models/") + litellm_model_name = "gemini/" + stripped_model_name + litellm_model_names.append(litellm_model_name) + return litellm_model_names diff --git a/litellm/llms/groq/chat/transformation.py b/litellm/llms/groq/chat/transformation.py index 5b24f7d112..b0ee69bed2 100644 --- a/litellm/llms/groq/chat/transformation.py +++ b/litellm/llms/groq/chat/transformation.py @@ -18,7 +18,6 @@ from ...openai.chat.gpt_transformation import OpenAIGPTConfig class GroqChatConfig(OpenAIGPTConfig): - frequency_penalty: Optional[int] = None function_call: Optional[Union[str, dict]] = None functions: Optional[list] = None diff --git a/litellm/llms/groq/stt/transformation.py b/litellm/llms/groq/stt/transformation.py index c4dbd8d0ca..b467fab14f 100644 --- a/litellm/llms/groq/stt/transformation.py +++ b/litellm/llms/groq/stt/transformation.py @@ -9,7 +9,6 @@ import litellm class GroqSTTConfig: - frequency_penalty: Optional[int] = None function_call: Optional[Union[str, dict]] = None functions: Optional[list] = None diff --git a/litellm/llms/huggingface/chat/transformation.py b/litellm/llms/huggingface/chat/transformation.py index 858fda473e..082960b2c2 100644 --- a/litellm/llms/huggingface/chat/transformation.py +++ b/litellm/llms/huggingface/chat/transformation.py @@ -40,17 +40,17 @@ class HuggingfaceChatConfig(BaseConfig): Reference: https://huggingface.github.io/text-generation-inference/#/Text%20Generation%20Inference/compat_generate """ - hf_task: Optional[hf_tasks] = ( - None # litellm-specific param, used to know the api spec to use when calling huggingface api - ) + hf_task: Optional[ + hf_tasks + ] = None # litellm-specific param, used to know the api spec to use when calling huggingface api best_of: Optional[int] = None decoder_input_details: Optional[bool] = None details: Optional[bool] = True # enables returning logprobs + best of max_new_tokens: Optional[int] = None repetition_penalty: Optional[float] = None - return_full_text: Optional[bool] = ( - False # by default don't return the input as part of the output - ) + return_full_text: Optional[ + bool + ] = False # by default don't return the input as part of the output seed: Optional[int] = None temperature: Optional[float] = None top_k: Optional[int] = None @@ -120,9 +120,9 @@ class HuggingfaceChatConfig(BaseConfig): optional_params["top_p"] = value if param == "n": optional_params["best_of"] = value - optional_params["do_sample"] = ( - True # Need to sample if you want best of for hf inference endpoints - ) + optional_params[ + "do_sample" + ] = True # Need to sample if you want best of for hf inference endpoints if param == "stream": optional_params["stream"] = value if param == "stop": @@ -362,9 +362,9 @@ class HuggingfaceChatConfig(BaseConfig): "content-type": "application/json", } if api_key is not None: - default_headers["Authorization"] = ( - f"Bearer {api_key}" # Huggingface Inference Endpoint default is to accept bearer tokens - ) + default_headers[ + "Authorization" + ] = f"Bearer {api_key}" # Huggingface Inference Endpoint default is to accept bearer tokens headers = {**headers, **default_headers} return headers diff --git a/litellm/llms/maritalk.py b/litellm/llms/maritalk.py index 5f2b8d71bc..418d13b344 100644 --- a/litellm/llms/maritalk.py +++ b/litellm/llms/maritalk.py @@ -17,7 +17,6 @@ class MaritalkError(BaseLLMException): class MaritalkConfig(OpenAIGPTConfig): - def __init__( self, frequency_penalty: Optional[float] = None, diff --git a/litellm/llms/mistral/mistral_chat_transformation.py b/litellm/llms/mistral/mistral_chat_transformation.py index 3e7a97c92f..67d88868d3 100644 --- a/litellm/llms/mistral/mistral_chat_transformation.py +++ b/litellm/llms/mistral/mistral_chat_transformation.py @@ -80,6 +80,7 @@ class MistralConfig(OpenAIGPTConfig): "temperature", "top_p", "max_tokens", + "max_completion_tokens", "tools", "tool_choice", "seed", @@ -105,6 +106,10 @@ class MistralConfig(OpenAIGPTConfig): for param, value in non_default_params.items(): if param == "max_tokens": optional_params["max_tokens"] = value + if ( + param == "max_completion_tokens" + ): # max_completion_tokens should take priority + optional_params["max_tokens"] = value if param == "tools": optional_params["tools"] = value if param == "stream" and value is True: diff --git a/litellm/llms/ollama/completion/transformation.py b/litellm/llms/ollama/completion/transformation.py index b4db95cfa1..b007bbb2bc 100644 --- a/litellm/llms/ollama/completion/transformation.py +++ b/litellm/llms/ollama/completion/transformation.py @@ -89,9 +89,9 @@ class OllamaConfig(BaseConfig): repeat_penalty: Optional[float] = None temperature: Optional[float] = None seed: Optional[int] = None - stop: Optional[list] = ( - None # stop is a list based on this - https://github.com/ollama/ollama/pull/442 - ) + stop: Optional[ + list + ] = None # stop is a list based on this - https://github.com/ollama/ollama/pull/442 tfs_z: Optional[float] = None num_predict: Optional[int] = None top_k: Optional[int] = None diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index c765f97979..1fde23c9c2 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -391,7 +391,6 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig): class OpenAIChatCompletionStreamingHandler(BaseModelResponseIterator): - def chunk_parser(self, chunk: dict) -> ModelResponseStream: try: return ModelResponseStream( diff --git a/litellm/llms/openai/completion/handler.py b/litellm/llms/openai/completion/handler.py index 2e60f55b57..fa31c487cd 100644 --- a/litellm/llms/openai/completion/handler.py +++ b/litellm/llms/openai/completion/handler.py @@ -220,7 +220,6 @@ class OpenAITextCompletion(BaseLLM): client=None, organization=None, ): - if client is None: openai_client = OpenAI( api_key=api_key, diff --git a/litellm/llms/openai/completion/transformation.py b/litellm/llms/openai/completion/transformation.py index 1aef72d3fa..43fbc1f219 100644 --- a/litellm/llms/openai/completion/transformation.py +++ b/litellm/llms/openai/completion/transformation.py @@ -111,9 +111,9 @@ class OpenAITextCompletionConfig(BaseTextCompletionConfig, OpenAIGPTConfig): if "model" in response_object: model_response_object.model = response_object["model"] - model_response_object._hidden_params["original_response"] = ( - response_object # track original response, if users make a litellm.text_completion() request, we can return the original response - ) + model_response_object._hidden_params[ + "original_response" + ] = response_object # track original response, if users make a litellm.text_completion() request, we can return the original response return model_response_object except Exception as e: raise e diff --git a/litellm/llms/openai/cost_calculation.py b/litellm/llms/openai/cost_calculation.py index 0c26fd7448..304c444e37 100644 --- a/litellm/llms/openai/cost_calculation.py +++ b/litellm/llms/openai/cost_calculation.py @@ -6,6 +6,7 @@ Helper util for handling openai-specific cost calculation from typing import Literal, Optional, Tuple from litellm._logging import verbose_logger +from litellm.litellm_core_utils.llm_cost_calc.utils import generic_cost_per_token from litellm.types.utils import CallTypes, Usage from litellm.utils import get_model_info @@ -28,52 +29,53 @@ def cost_per_token(model: str, usage: Usage) -> Tuple[float, float]: Returns: Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd """ - ## GET MODEL INFO - model_info = get_model_info(model=model, custom_llm_provider="openai") ## CALCULATE INPUT COST - ### Non-cached text tokens - non_cached_text_tokens = usage.prompt_tokens - cached_tokens: Optional[int] = None - if usage.prompt_tokens_details and usage.prompt_tokens_details.cached_tokens: - cached_tokens = usage.prompt_tokens_details.cached_tokens - non_cached_text_tokens = non_cached_text_tokens - cached_tokens - prompt_cost: float = non_cached_text_tokens * model_info["input_cost_per_token"] - ## Prompt Caching cost calculation - if model_info.get("cache_read_input_token_cost") is not None and cached_tokens: - # Note: We read ._cache_read_input_tokens from the Usage - since cost_calculator.py standardizes the cache read tokens on usage._cache_read_input_tokens - prompt_cost += cached_tokens * ( - model_info.get("cache_read_input_token_cost", 0) or 0 - ) + return generic_cost_per_token( + model=model, usage=usage, custom_llm_provider="openai" + ) + # ### Non-cached text tokens + # non_cached_text_tokens = usage.prompt_tokens + # cached_tokens: Optional[int] = None + # if usage.prompt_tokens_details and usage.prompt_tokens_details.cached_tokens: + # cached_tokens = usage.prompt_tokens_details.cached_tokens + # non_cached_text_tokens = non_cached_text_tokens - cached_tokens + # prompt_cost: float = non_cached_text_tokens * model_info["input_cost_per_token"] + # ## Prompt Caching cost calculation + # if model_info.get("cache_read_input_token_cost") is not None and cached_tokens: + # # Note: We read ._cache_read_input_tokens from the Usage - since cost_calculator.py standardizes the cache read tokens on usage._cache_read_input_tokens + # prompt_cost += cached_tokens * ( + # model_info.get("cache_read_input_token_cost", 0) or 0 + # ) - _audio_tokens: Optional[int] = ( - usage.prompt_tokens_details.audio_tokens - if usage.prompt_tokens_details is not None - else None - ) - _audio_cost_per_token: Optional[float] = model_info.get( - "input_cost_per_audio_token" - ) - if _audio_tokens is not None and _audio_cost_per_token is not None: - audio_cost: float = _audio_tokens * _audio_cost_per_token - prompt_cost += audio_cost + # _audio_tokens: Optional[int] = ( + # usage.prompt_tokens_details.audio_tokens + # if usage.prompt_tokens_details is not None + # else None + # ) + # _audio_cost_per_token: Optional[float] = model_info.get( + # "input_cost_per_audio_token" + # ) + # if _audio_tokens is not None and _audio_cost_per_token is not None: + # audio_cost: float = _audio_tokens * _audio_cost_per_token + # prompt_cost += audio_cost - ## CALCULATE OUTPUT COST - completion_cost: float = ( - usage["completion_tokens"] * model_info["output_cost_per_token"] - ) - _output_cost_per_audio_token: Optional[float] = model_info.get( - "output_cost_per_audio_token" - ) - _output_audio_tokens: Optional[int] = ( - usage.completion_tokens_details.audio_tokens - if usage.completion_tokens_details is not None - else None - ) - if _output_cost_per_audio_token is not None and _output_audio_tokens is not None: - audio_cost = _output_audio_tokens * _output_cost_per_audio_token - completion_cost += audio_cost + # ## CALCULATE OUTPUT COST + # completion_cost: float = ( + # usage["completion_tokens"] * model_info["output_cost_per_token"] + # ) + # _output_cost_per_audio_token: Optional[float] = model_info.get( + # "output_cost_per_audio_token" + # ) + # _output_audio_tokens: Optional[int] = ( + # usage.completion_tokens_details.audio_tokens + # if usage.completion_tokens_details is not None + # else None + # ) + # if _output_cost_per_audio_token is not None and _output_audio_tokens is not None: + # audio_cost = _output_audio_tokens * _output_cost_per_audio_token + # completion_cost += audio_cost - return prompt_cost, completion_cost + # return prompt_cost, completion_cost def cost_per_second( diff --git a/litellm/llms/openai/fine_tuning/handler.py b/litellm/llms/openai/fine_tuning/handler.py index 97b237c757..2b697f85d2 100644 --- a/litellm/llms/openai/fine_tuning/handler.py +++ b/litellm/llms/openai/fine_tuning/handler.py @@ -28,14 +28,7 @@ class OpenAIFineTuningAPI: _is_async: bool = False, api_version: Optional[str] = None, litellm_params: Optional[dict] = None, - ) -> Optional[ - Union[ - OpenAI, - AsyncOpenAI, - AzureOpenAI, - AsyncAzureOpenAI, - ] - ]: + ) -> Optional[Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI,]]: received_args = locals() openai_client: Optional[ Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI] diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index deb70b481e..0545542ead 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -266,7 +266,6 @@ class OpenAIConfig(BaseConfig): api_key: Optional[str] = None, json_mode: Optional[bool] = None, ) -> ModelResponse: - logging_obj.post_call(original_response=raw_response.text) logging_obj.model_call_details["response_headers"] = raw_response.headers final_response_obj = cast( @@ -320,7 +319,6 @@ class OpenAIChatCompletionResponseIterator(BaseModelResponseIterator): class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): - def __init__(self) -> None: super().__init__() @@ -513,7 +511,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): custom_llm_provider: Optional[str] = None, drop_params: Optional[bool] = None, ): - super().completion() try: fake_stream: bool = False @@ -553,7 +550,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): for _ in range( 2 ): # if call fails due to alternating messages, retry with reformatted message - if provider_config is not None: data = provider_config.transform_request( model=model, @@ -649,13 +645,14 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): }, ) - headers, response = ( - self.make_sync_openai_chat_completion_request( - openai_client=openai_client, - data=data, - timeout=timeout, - logging_obj=logging_obj, - ) + ( + headers, + response, + ) = self.make_sync_openai_chat_completion_request( + openai_client=openai_client, + data=data, + timeout=timeout, + logging_obj=logging_obj, ) logging_obj.model_call_details["response_headers"] = headers @@ -763,7 +760,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): for _ in range( 2 ): # if call fails due to alternating messages, retry with reformatted message - try: openai_aclient: AsyncOpenAI = self._get_openai_client( # type: ignore is_async=True, @@ -973,7 +969,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): except ( Exception ) as e: # need to exception handle here. async exceptions don't get caught in sync functions. - if isinstance(e, OpenAIError): raise e @@ -1246,7 +1241,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): ): response = None try: - openai_aclient = self._get_openai_client( is_async=True, api_key=api_key, @@ -1333,7 +1327,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): ) return convert_to_model_response_object(response_object=response, model_response_object=model_response, response_type="image_generation") # type: ignore except OpenAIError as e: - ## LOGGING logging_obj.post_call( input=prompt, @@ -1372,7 +1365,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): aspeech: Optional[bool] = None, client=None, ) -> HttpxBinaryResponseContent: - if aspeech is not None and aspeech is True: return self.async_audio_speech( model=model, @@ -1419,7 +1411,6 @@ class OpenAIChatCompletion(BaseLLM, BaseOpenAILLM): timeout: Union[float, httpx.Timeout], client=None, ) -> HttpxBinaryResponseContent: - openai_client = cast( AsyncOpenAI, self._get_openai_client( diff --git a/litellm/llms/openai/transcriptions/gpt_transformation.py b/litellm/llms/openai/transcriptions/gpt_transformation.py new file mode 100644 index 0000000000..796e10f515 --- /dev/null +++ b/litellm/llms/openai/transcriptions/gpt_transformation.py @@ -0,0 +1,34 @@ +from typing import List + +from litellm.types.llms.openai import OpenAIAudioTranscriptionOptionalParams +from litellm.types.utils import FileTypes + +from .whisper_transformation import OpenAIWhisperAudioTranscriptionConfig + + +class OpenAIGPTAudioTranscriptionConfig(OpenAIWhisperAudioTranscriptionConfig): + def get_supported_openai_params( + self, model: str + ) -> List[OpenAIAudioTranscriptionOptionalParams]: + """ + Get the supported OpenAI params for the `gpt-4o-transcribe` models + """ + return [ + "language", + "prompt", + "response_format", + "temperature", + "include", + ] + + def transform_audio_transcription_request( + self, + model: str, + audio_file: FileTypes, + optional_params: dict, + litellm_params: dict, + ) -> dict: + """ + Transform the audio transcription request + """ + return {"model": model, "file": audio_file, **optional_params} diff --git a/litellm/llms/openai/transcriptions/handler.py b/litellm/llms/openai/transcriptions/handler.py index d9dd3c123b..78a913cbf3 100644 --- a/litellm/llms/openai/transcriptions/handler.py +++ b/litellm/llms/openai/transcriptions/handler.py @@ -7,6 +7,9 @@ from pydantic import BaseModel import litellm from litellm.litellm_core_utils.audio_utils.utils import get_audio_file_name from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.llms.base_llm.audio_transcription.transformation import ( + BaseAudioTranscriptionConfig, +) from litellm.types.utils import FileTypes from litellm.utils import ( TranscriptionResponse, @@ -75,6 +78,7 @@ class OpenAIAudioTranscription(OpenAIChatCompletion): model: str, audio_file: FileTypes, optional_params: dict, + litellm_params: dict, model_response: TranscriptionResponse, timeout: float, max_retries: int, @@ -83,16 +87,24 @@ class OpenAIAudioTranscription(OpenAIChatCompletion): api_base: Optional[str], client=None, atranscription: bool = False, + provider_config: Optional[BaseAudioTranscriptionConfig] = None, ) -> TranscriptionResponse: - data = {"model": model, "file": audio_file, **optional_params} - - if "response_format" not in data or ( - data["response_format"] == "text" or data["response_format"] == "json" - ): - data["response_format"] = ( - "verbose_json" # ensures 'duration' is received - used for cost calculation + """ + Handle audio transcription request + """ + if provider_config is not None: + data = provider_config.transform_audio_transcription_request( + model=model, + audio_file=audio_file, + optional_params=optional_params, + litellm_params=litellm_params, ) + if isinstance(data, bytes): + raise ValueError("OpenAI transformation route requires a dict") + else: + data = {"model": model, "file": audio_file, **optional_params} + if atranscription is True: return self.async_audio_transcriptions( # type: ignore audio_file=audio_file, diff --git a/litellm/llms/openai/transcriptions/whisper_transformation.py b/litellm/llms/openai/transcriptions/whisper_transformation.py new file mode 100644 index 0000000000..2d3d611dac --- /dev/null +++ b/litellm/llms/openai/transcriptions/whisper_transformation.py @@ -0,0 +1,97 @@ +from typing import List, Optional, Union + +from httpx import Headers + +from litellm.llms.base_llm.audio_transcription.transformation import ( + BaseAudioTranscriptionConfig, +) +from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.secret_managers.main import get_secret_str +from litellm.types.llms.openai import ( + AllMessageValues, + OpenAIAudioTranscriptionOptionalParams, +) +from litellm.types.utils import FileTypes + +from ..common_utils import OpenAIError + + +class OpenAIWhisperAudioTranscriptionConfig(BaseAudioTranscriptionConfig): + def get_supported_openai_params( + self, model: str + ) -> List[OpenAIAudioTranscriptionOptionalParams]: + """ + Get the supported OpenAI params for the `whisper-1` models + """ + return [ + "language", + "prompt", + "response_format", + "temperature", + "timestamp_granularities", + ] + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + model: str, + drop_params: bool, + ) -> dict: + """ + Map the OpenAI params to the Whisper params + """ + supported_params = self.get_supported_openai_params(model) + for k, v in non_default_params.items(): + if k in supported_params: + optional_params[k] = v + return optional_params + + def validate_environment( + self, + headers: dict, + model: str, + messages: List[AllMessageValues], + optional_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> dict: + api_key = api_key or get_secret_str("OPENAI_API_KEY") + + auth_header = { + "Authorization": f"Bearer {api_key}", + } + + headers.update(auth_header) + return headers + + def transform_audio_transcription_request( + self, + model: str, + audio_file: FileTypes, + optional_params: dict, + litellm_params: dict, + ) -> dict: + """ + Transform the audio transcription request + """ + + data = {"model": model, "file": audio_file, **optional_params} + + if "response_format" not in data or ( + data["response_format"] == "text" or data["response_format"] == "json" + ): + data[ + "response_format" + ] = "verbose_json" # ensures 'duration' is received - used for cost calculation + + return data + + def get_error_class( + self, error_message: str, status_code: int, headers: Union[dict, Headers] + ) -> BaseLLMException: + return OpenAIError( + status_code=status_code, + message=error_message, + headers=headers, + ) diff --git a/litellm/llms/openrouter/chat/transformation.py b/litellm/llms/openrouter/chat/transformation.py index 4b95ec87cf..0b47167524 100644 --- a/litellm/llms/openrouter/chat/transformation.py +++ b/litellm/llms/openrouter/chat/transformation.py @@ -19,7 +19,6 @@ from ..common_utils import OpenRouterException class OpenrouterConfig(OpenAIGPTConfig): - def map_openai_params( self, non_default_params: dict, @@ -42,9 +41,9 @@ class OpenrouterConfig(OpenAIGPTConfig): extra_body["models"] = models if route is not None: extra_body["route"] = route - mapped_openai_params["extra_body"] = ( - extra_body # openai client supports `extra_body` param - ) + mapped_openai_params[ + "extra_body" + ] = extra_body # openai client supports `extra_body` param return mapped_openai_params def get_error_class( @@ -70,7 +69,6 @@ class OpenrouterConfig(OpenAIGPTConfig): class OpenRouterChatCompletionStreamingHandler(BaseModelResponseIterator): - def chunk_parser(self, chunk: dict) -> ModelResponseStream: try: new_choices = [] @@ -84,5 +82,11 @@ class OpenRouterChatCompletionStreamingHandler(BaseModelResponseIterator): model=chunk["model"], choices=new_choices, ) + except KeyError as e: + raise OpenRouterException( + message=f"KeyError: {e}, Got unexpected response from OpenRouter: {chunk}", + status_code=400, + headers={"Content-Type": "application/json"}, + ) except Exception as e: raise e diff --git a/litellm/llms/petals/completion/transformation.py b/litellm/llms/petals/completion/transformation.py index 08ec15de33..a9e37d27fc 100644 --- a/litellm/llms/petals/completion/transformation.py +++ b/litellm/llms/petals/completion/transformation.py @@ -37,9 +37,9 @@ class PetalsConfig(BaseConfig): """ max_length: Optional[int] = None - max_new_tokens: Optional[int] = ( - litellm.max_tokens - ) # petals requires max tokens to be set + max_new_tokens: Optional[ + int + ] = litellm.max_tokens # petals requires max tokens to be set do_sample: Optional[bool] = None temperature: Optional[float] = None top_k: Optional[int] = None diff --git a/litellm/llms/predibase/chat/handler.py b/litellm/llms/predibase/chat/handler.py index 43f4b06745..cd80fa53e4 100644 --- a/litellm/llms/predibase/chat/handler.py +++ b/litellm/llms/predibase/chat/handler.py @@ -394,7 +394,6 @@ class PredibaseChatCompletion: logger_fn=None, headers={}, ) -> ModelResponse: - async_handler = get_async_httpx_client( llm_provider=litellm.LlmProviders.PREDIBASE, params={"timeout": timeout}, diff --git a/litellm/llms/predibase/chat/transformation.py b/litellm/llms/predibase/chat/transformation.py index f574238696..f1a2163d24 100644 --- a/litellm/llms/predibase/chat/transformation.py +++ b/litellm/llms/predibase/chat/transformation.py @@ -30,9 +30,9 @@ class PredibaseConfig(BaseConfig): 256 # openai default - requests hang if max_new_tokens not given ) repetition_penalty: Optional[float] = None - return_full_text: Optional[bool] = ( - False # by default don't return the input as part of the output - ) + return_full_text: Optional[ + bool + ] = False # by default don't return the input as part of the output seed: Optional[int] = None stop: Optional[List[str]] = None temperature: Optional[float] = None @@ -99,9 +99,9 @@ class PredibaseConfig(BaseConfig): optional_params["top_p"] = value if param == "n": optional_params["best_of"] = value - optional_params["do_sample"] = ( - True # Need to sample if you want best of for hf inference endpoints - ) + optional_params[ + "do_sample" + ] = True # Need to sample if you want best of for hf inference endpoints if param == "stream": optional_params["stream"] = value if param == "stop": diff --git a/litellm/llms/replicate/chat/handler.py b/litellm/llms/replicate/chat/handler.py index f52eb2ee05..526f376b89 100644 --- a/litellm/llms/replicate/chat/handler.py +++ b/litellm/llms/replicate/chat/handler.py @@ -244,7 +244,6 @@ async def async_completion( print_verbose, headers: dict, ) -> Union[ModelResponse, CustomStreamWrapper]: - prediction_url = replicate_config.get_complete_url( api_base=api_base, model=model, diff --git a/litellm/llms/sagemaker/chat/handler.py b/litellm/llms/sagemaker/chat/handler.py index c827a8a5f7..b86cda7aea 100644 --- a/litellm/llms/sagemaker/chat/handler.py +++ b/litellm/llms/sagemaker/chat/handler.py @@ -13,7 +13,6 @@ from .transformation import SagemakerChatConfig class SagemakerChatHandler(BaseAWSLLM): - def _load_credentials( self, optional_params: dict, @@ -128,7 +127,6 @@ class SagemakerChatHandler(BaseAWSLLM): headers: dict = {}, client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, ): - # pop streaming if it's in the optional params as 'stream' raises an error with sagemaker credentials, aws_region_name = self._load_credentials(optional_params) inference_params = deepcopy(optional_params) diff --git a/litellm/llms/sagemaker/common_utils.py b/litellm/llms/sagemaker/common_utils.py index 9884f420c3..ce0c6c9506 100644 --- a/litellm/llms/sagemaker/common_utils.py +++ b/litellm/llms/sagemaker/common_utils.py @@ -34,7 +34,6 @@ class AWSEventStreamDecoder: def _chunk_parser_messages_api( self, chunk_data: dict ) -> StreamingChatCompletionChunk: - openai_chunk = StreamingChatCompletionChunk(**chunk_data) return openai_chunk @@ -192,7 +191,6 @@ class AWSEventStreamDecoder: def get_response_stream_shape(): global _response_stream_shape_cache if _response_stream_shape_cache is None: - from botocore.loaders import Loader from botocore.model import ServiceModel diff --git a/litellm/llms/sagemaker/completion/handler.py b/litellm/llms/sagemaker/completion/handler.py index 909caf73c3..296689c31c 100644 --- a/litellm/llms/sagemaker/completion/handler.py +++ b/litellm/llms/sagemaker/completion/handler.py @@ -1,6 +1,6 @@ import json from copy import deepcopy -from typing import Any, Callable, List, Optional, Union +from typing import Any, Callable, List, Optional, Union, cast import httpx @@ -35,7 +35,6 @@ os.environ['AWS_SECRET_ACCESS_KEY'] = "" # set os.environ['AWS_REGION_NAME'] = class SagemakerLLM(BaseAWSLLM): - def _load_credentials( self, optional_params: dict, @@ -154,7 +153,6 @@ class SagemakerLLM(BaseAWSLLM): acompletion: bool = False, headers: dict = {}, ): - # pop streaming if it's in the optional params as 'stream' raises an error with sagemaker credentials, aws_region_name = self._load_credentials(optional_params) inference_params = deepcopy(optional_params) @@ -437,10 +435,14 @@ class SagemakerLLM(BaseAWSLLM): prepared_request.headers.update( {"X-Amzn-SageMaker-Inference-Component": model_id} ) + + if not prepared_request.body: + raise ValueError("Prepared request body is empty") + completion_stream = await self.make_async_call( api_base=prepared_request.url, headers=prepared_request.headers, # type: ignore - data=prepared_request.body, + data=cast(str, prepared_request.body), logging_obj=logging_obj, ) streaming_response = CustomStreamWrapper( @@ -625,7 +627,7 @@ class SagemakerLLM(BaseAWSLLM): response = client.invoke_endpoint( EndpointName={model}, ContentType="application/json", - Body={data}, # type: ignore + Body=f"{data!r}", # Use !r for safe representation CustomAttributes="accept_eula=true", )""" # type: ignore logging_obj.pre_call( diff --git a/litellm/llms/sagemaker/completion/transformation.py b/litellm/llms/sagemaker/completion/transformation.py index d0ab5d0697..df3d028c99 100644 --- a/litellm/llms/sagemaker/completion/transformation.py +++ b/litellm/llms/sagemaker/completion/transformation.py @@ -19,6 +19,7 @@ from litellm.litellm_core_utils.prompt_templates.factory import ( from litellm.llms.base_llm.chat.transformation import BaseConfig, BaseLLMException from litellm.types.llms.openai import AllMessageValues from litellm.types.utils import ModelResponse, Usage +from litellm.utils import token_counter from ..common_utils import SagemakerError @@ -88,9 +89,9 @@ class SagemakerConfig(BaseConfig): optional_params["top_p"] = value if param == "n": optional_params["best_of"] = value - optional_params["do_sample"] = ( - True # Need to sample if you want best of for hf inference endpoints - ) + optional_params[ + "do_sample" + ] = True # Need to sample if you want best of for hf inference endpoints if param == "stream": optional_params["stream"] = value if param == "stop": @@ -238,9 +239,12 @@ class SagemakerConfig(BaseConfig): ) ## CALCULATING USAGE - baseten charges on time, not tokens - have some mapping of cost here. - prompt_tokens = len(encoding.encode(prompt)) - completion_tokens = len( - encoding.encode(model_response["choices"][0]["message"].get("content", "")) + prompt_tokens = token_counter( + text=prompt, count_response_tokens=True + ) # doesn't apply any default token count from openai's chat template + completion_tokens = token_counter( + text=model_response["choices"][0]["message"].get("content", ""), + count_response_tokens=True, ) model_response.created = int(time.time()) diff --git a/litellm/llms/together_ai/rerank/transformation.py b/litellm/llms/together_ai/rerank/transformation.py index 4714376979..1fdb772add 100644 --- a/litellm/llms/together_ai/rerank/transformation.py +++ b/litellm/llms/together_ai/rerank/transformation.py @@ -19,7 +19,6 @@ from litellm.types.rerank import ( class TogetherAIRerankConfig: def _transform_response(self, response: dict) -> RerankResponse: - _billed_units = RerankBilledUnits(**response.get("usage", {})) _tokens = RerankTokens(**response.get("usage", {})) rerank_meta = RerankResponseMeta(billed_units=_billed_units, tokens=_tokens) diff --git a/litellm/llms/topaz/common_utils.py b/litellm/llms/topaz/common_utils.py index 4ef2315db4..0252585922 100644 --- a/litellm/llms/topaz/common_utils.py +++ b/litellm/llms/topaz/common_utils.py @@ -11,7 +11,9 @@ class TopazException(BaseLLMException): class TopazModelInfo(BaseLLMModelInfo): - def get_models(self) -> List[str]: + def get_models( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> List[str]: return [ "topaz/Standard V2", "topaz/Low Resolution V2", diff --git a/litellm/llms/topaz/image_variations/transformation.py b/litellm/llms/topaz/image_variations/transformation.py index 8b95deed04..6188101015 100644 --- a/litellm/llms/topaz/image_variations/transformation.py +++ b/litellm/llms/topaz/image_variations/transformation.py @@ -121,7 +121,6 @@ class TopazImageVariationConfig(BaseImageVariationConfig): optional_params: dict, headers: dict, ) -> HttpHandlerRequestFields: - request_params = HttpHandlerRequestFields( files={"image": self.prepare_file_tuple(image)}, data=optional_params, @@ -134,7 +133,6 @@ class TopazImageVariationConfig(BaseImageVariationConfig): image_content: bytes, response_ms: float, ) -> ImageResponse: - # Convert to base64 base64_image = base64.b64encode(image_content).decode("utf-8") diff --git a/litellm/llms/triton/completion/transformation.py b/litellm/llms/triton/completion/transformation.py index 56151f89ef..46b607d455 100644 --- a/litellm/llms/triton/completion/transformation.py +++ b/litellm/llms/triton/completion/transformation.py @@ -244,7 +244,6 @@ class TritonInferConfig(TritonConfig): litellm_params: dict, headers: dict, ) -> dict: - text_input = messages[0].get("content", "") data_for_triton = { "inputs": [ diff --git a/litellm/llms/vertex_ai/batches/handler.py b/litellm/llms/vertex_ai/batches/handler.py index b82268bef6..dc3f93857a 100644 --- a/litellm/llms/vertex_ai/batches/handler.py +++ b/litellm/llms/vertex_ai/batches/handler.py @@ -35,7 +35,6 @@ class VertexAIBatchPrediction(VertexLLM): timeout: Union[float, httpx.Timeout], max_retries: Optional[int], ) -> Union[LiteLLMBatch, Coroutine[Any, Any, LiteLLMBatch]]: - sync_handler = _get_httpx_client() access_token, project_id = self._ensure_access_token( @@ -69,10 +68,8 @@ class VertexAIBatchPrediction(VertexLLM): "Authorization": f"Bearer {access_token}", } - vertex_batch_request: VertexAIBatchPredictionJob = ( - VertexAIBatchTransformation.transform_openai_batch_request_to_vertex_ai_batch_request( - request=create_batch_data - ) + vertex_batch_request: VertexAIBatchPredictionJob = VertexAIBatchTransformation.transform_openai_batch_request_to_vertex_ai_batch_request( + request=create_batch_data ) if _is_async is True: diff --git a/litellm/llms/vertex_ai/common_utils.py b/litellm/llms/vertex_ai/common_utils.py index 0d792527b4..f2cd1ef557 100644 --- a/litellm/llms/vertex_ai/common_utils.py +++ b/litellm/llms/vertex_ai/common_utils.py @@ -3,7 +3,9 @@ from typing import Dict, List, Literal, Optional, Tuple, Union import httpx +import litellm from litellm import supports_response_schema, supports_system_messages, verbose_logger +from litellm.constants import DEFAULT_MAX_RECURSE_DEPTH from litellm.llms.base_llm.chat.transformation import BaseLLMException from litellm.types.llms.vertex_ai import PartType @@ -28,6 +30,10 @@ def get_supports_system_message( supports_system_message = supports_system_messages( model=model, custom_llm_provider=_custom_llm_provider ) + + # Vertex Models called in the `/gemini` request/response format also support system messages + if litellm.VertexGeminiConfig._is_model_gemini_spec_model(model): + supports_system_message = True except Exception as e: verbose_logger.warning( "Unable to identify if system message supported. Defaulting to 'False'. Received error message - {}\nAdd it here - https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json".format( @@ -70,6 +76,8 @@ def _get_vertex_url( ) -> Tuple[str, str]: url: Optional[str] = None endpoint: Optional[str] = None + + model = litellm.VertexGeminiConfig.get_model_for_vertex_ai_url(model=model) if mode == "chat": ### SET RUNTIME ENDPOINT ### endpoint = "generateContent" @@ -170,7 +178,7 @@ def _build_vertex_schema(parameters: dict): # * https://github.com/pydantic/pydantic/issues/1270 # * https://stackoverflow.com/a/58841311 # * https://github.com/pydantic/pydantic/discussions/4872 - convert_to_nullable(parameters) + convert_anyof_null_to_nullable(parameters) add_object_type(parameters) # Postprocessing # 4. Suppress unnecessary title generation: @@ -221,34 +229,41 @@ def unpack_defs(schema, defs): continue -def convert_to_nullable(schema): - anyof = schema.pop("anyOf", None) +def convert_anyof_null_to_nullable(schema, depth=0): + if depth > DEFAULT_MAX_RECURSE_DEPTH: + raise ValueError( + f"Max depth of {DEFAULT_MAX_RECURSE_DEPTH} exceeded while processing schema. Please check the schema for excessive nesting." + ) + """ Converts null objects within anyOf by removing them and adding nullable to all remaining objects """ + anyof = schema.get("anyOf", None) if anyof is not None: - if len(anyof) != 2: + contains_null = False + for atype in anyof: + if atype == {"type": "null"}: + # remove null type + anyof.remove(atype) + contains_null = True + + if len(anyof) == 0: + # Edge case: response schema with only null type present is invalid in Vertex AI raise ValueError( - "Invalid input: Type Unions are not supported, except for `Optional` types. " - "Please provide an `Optional` type or a non-Union type." + "Invalid input: AnyOf schema with only null type is not supported. " + "Please provide a non-null type." ) - a, b = anyof - if a == {"type": "null"}: - schema.update(b) - elif b == {"type": "null"}: - schema.update(a) - else: - raise ValueError( - "Invalid input: Type Unions are not supported, except for `Optional` types. " - "Please provide an `Optional` type or a non-Union type." - ) - schema["nullable"] = True + + if contains_null: + # set all types to nullable following guidance found here: https://cloud.google.com/vertex-ai/generative-ai/docs/samples/generativeaionvertexai-gemini-controlled-generation-response-schema-3#generativeaionvertexai_gemini_controlled_generation_response_schema_3-python + for atype in anyof: + atype["nullable"] = True properties = schema.get("properties", None) if properties is not None: for name, value in properties.items(): - convert_to_nullable(value) + convert_anyof_null_to_nullable(value, depth=depth + 1) items = schema.get("items", None) if items is not None: - convert_to_nullable(items) + convert_anyof_null_to_nullable(items, depth=depth + 1) def add_object_type(schema): diff --git a/litellm/llms/vertex_ai/cost_calculator.py b/litellm/llms/vertex_ai/cost_calculator.py index fd23886045..119ba2b036 100644 --- a/litellm/llms/vertex_ai/cost_calculator.py +++ b/litellm/llms/vertex_ai/cost_calculator.py @@ -4,7 +4,11 @@ from typing import Literal, Optional, Tuple, Union import litellm from litellm import verbose_logger -from litellm.litellm_core_utils.llm_cost_calc.utils import _is_above_128k +from litellm.litellm_core_utils.llm_cost_calc.utils import ( + _is_above_128k, + generic_cost_per_token, +) +from litellm.types.utils import ModelInfo, Usage """ Gemini pricing covers: @@ -20,7 +24,7 @@ Vertex AI -> character based pricing Google AI Studio -> token based pricing """ -models_without_dynamic_pricing = ["gemini-1.0-pro", "gemini-pro"] +models_without_dynamic_pricing = ["gemini-1.0-pro", "gemini-pro", "gemini-2"] def cost_router( @@ -46,14 +50,15 @@ def cost_router( call_type == "embedding" or call_type == "aembedding" ): return "cost_per_token" + elif custom_llm_provider == "vertex_ai" and ("gemini-2" in model): + return "cost_per_token" return "cost_per_character" def cost_per_character( model: str, custom_llm_provider: str, - prompt_tokens: float, - completion_tokens: float, + usage: Usage, prompt_characters: Optional[float] = None, completion_characters: Optional[float] = None, ) -> Tuple[float, float]: @@ -86,8 +91,7 @@ def cost_per_character( prompt_cost, _ = cost_per_token( model=model, custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage, ) else: try: @@ -124,8 +128,7 @@ def cost_per_character( prompt_cost, _ = cost_per_token( model=model, custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage, ) ## CALCULATE OUTPUT COST @@ -133,10 +136,10 @@ def cost_per_character( _, completion_cost = cost_per_token( model=model, custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage, ) else: + completion_tokens = usage.completion_tokens try: if ( _is_above_128k(tokens=completion_characters * 4) # 1 token = 4 char @@ -172,18 +175,54 @@ def cost_per_character( _, completion_cost = cost_per_token( model=model, custom_llm_provider=custom_llm_provider, - prompt_tokens=prompt_tokens, - completion_tokens=completion_tokens, + usage=usage, ) return prompt_cost, completion_cost +def _handle_128k_pricing( + model_info: ModelInfo, + usage: Usage, +) -> Tuple[float, float]: + ## CALCULATE INPUT COST + input_cost_per_token_above_128k_tokens = model_info.get( + "input_cost_per_token_above_128k_tokens" + ) + output_cost_per_token_above_128k_tokens = model_info.get( + "output_cost_per_token_above_128k_tokens" + ) + + prompt_tokens = usage.prompt_tokens + completion_tokens = usage.completion_tokens + + if ( + _is_above_128k(tokens=prompt_tokens) + and input_cost_per_token_above_128k_tokens is not None + ): + prompt_cost = prompt_tokens * input_cost_per_token_above_128k_tokens + else: + prompt_cost = prompt_tokens * model_info["input_cost_per_token"] + + ## CALCULATE OUTPUT COST + output_cost_per_token_above_128k_tokens = model_info.get( + "output_cost_per_token_above_128k_tokens" + ) + if ( + _is_above_128k(tokens=completion_tokens) + and output_cost_per_token_above_128k_tokens is not None + ): + completion_cost = completion_tokens * output_cost_per_token_above_128k_tokens + else: + completion_cost = completion_tokens * model_info["output_cost_per_token"] + + return prompt_cost, completion_cost + + def cost_per_token( model: str, custom_llm_provider: str, - prompt_tokens: float, - completion_tokens: float, + usage: Usage, ) -> Tuple[float, float]: """ Calculates the cost per token for a given model, prompt tokens, and completion tokens. @@ -200,43 +239,30 @@ def cost_per_token( Raises: Exception if model requires >128k pricing, but model cost not mapped """ + ## GET MODEL INFO model_info = litellm.get_model_info( model=model, custom_llm_provider=custom_llm_provider ) - ## CALCULATE INPUT COST + ## HANDLE 128k+ PRICING + input_cost_per_token_above_128k_tokens = model_info.get( + "input_cost_per_token_above_128k_tokens" + ) + output_cost_per_token_above_128k_tokens = model_info.get( + "output_cost_per_token_above_128k_tokens" + ) if ( - _is_above_128k(tokens=prompt_tokens) - and model not in models_without_dynamic_pricing + input_cost_per_token_above_128k_tokens is not None + or output_cost_per_token_above_128k_tokens is not None ): - assert ( - "input_cost_per_token_above_128k_tokens" in model_info - and model_info["input_cost_per_token_above_128k_tokens"] is not None - ), "model info for model={} does not have pricing for > 128k tokens\nmodel_info={}".format( - model, model_info + return _handle_128k_pricing( + model_info=model_info, + usage=usage, ) - prompt_cost = ( - prompt_tokens * model_info["input_cost_per_token_above_128k_tokens"] - ) - else: - prompt_cost = prompt_tokens * model_info["input_cost_per_token"] - ## CALCULATE OUTPUT COST - if ( - _is_above_128k(tokens=completion_tokens) - and model not in models_without_dynamic_pricing - ): - assert ( - "output_cost_per_token_above_128k_tokens" in model_info - and model_info["output_cost_per_token_above_128k_tokens"] is not None - ), "model info for model={} does not have pricing for > 128k tokens\nmodel_info={}".format( - model, model_info - ) - completion_cost = ( - completion_tokens * model_info["output_cost_per_token_above_128k_tokens"] - ) - else: - completion_cost = completion_tokens * model_info["output_cost_per_token"] - - return prompt_cost, completion_cost + return generic_cost_per_token( + model=model, + custom_llm_provider=custom_llm_provider, + usage=usage, + ) diff --git a/litellm/llms/vertex_ai/files/handler.py b/litellm/llms/vertex_ai/files/handler.py index 266169cdfb..7000cf151d 100644 --- a/litellm/llms/vertex_ai/files/handler.py +++ b/litellm/llms/vertex_ai/files/handler.py @@ -49,10 +49,11 @@ class VertexAIFilesHandler(GCSBucketBase): service_account_json=gcs_logging_config["path_service_account"], ) bucket_name = gcs_logging_config["bucket_name"] - logging_payload, object_name = ( - vertex_ai_files_transformation.transform_openai_file_content_to_vertex_ai_file_content( - openai_file_content=create_file_data.get("file") - ) + ( + logging_payload, + object_name, + ) = vertex_ai_files_transformation.transform_openai_file_content_to_vertex_ai_file_content( + openai_file_content=create_file_data.get("file") ) gcs_upload_response = await self._log_json_data_on_gcs( headers=headers, diff --git a/litellm/llms/vertex_ai/fine_tuning/handler.py b/litellm/llms/vertex_ai/fine_tuning/handler.py index 3cf409c78e..7ea8527fd4 100644 --- a/litellm/llms/vertex_ai/fine_tuning/handler.py +++ b/litellm/llms/vertex_ai/fine_tuning/handler.py @@ -36,7 +36,6 @@ class VertexFineTuningAPI(VertexLLM): def convert_response_created_at(self, response: ResponseTuningJob): try: - create_time_str = response.get("createTime", "") or "" create_time_datetime = datetime.fromisoformat( create_time_str.replace("Z", "+00:00") @@ -65,9 +64,9 @@ class VertexFineTuningAPI(VertexLLM): ) if create_fine_tuning_job_data.validation_file: - supervised_tuning_spec["validation_dataset"] = ( - create_fine_tuning_job_data.validation_file - ) + supervised_tuning_spec[ + "validation_dataset" + ] = create_fine_tuning_job_data.validation_file _vertex_hyperparameters = ( self._transform_openai_hyperparameters_to_vertex_hyperparameters( @@ -175,7 +174,6 @@ class VertexFineTuningAPI(VertexLLM): headers: dict, request_data: FineTuneJobCreate, ): - try: verbose_logger.debug( "about to create fine tuning job: %s, request_data: %s", @@ -229,7 +227,6 @@ class VertexFineTuningAPI(VertexLLM): kwargs: Optional[dict] = None, original_hyperparameters: Optional[dict] = {}, ): - verbose_logger.debug( "creating fine tuning job, args= %s", create_fine_tuning_job_data ) @@ -346,9 +343,9 @@ class VertexFineTuningAPI(VertexLLM): elif "cachedContents" in request_route: _model = request_data.get("model") if _model is not None and "/publishers/google/models/" not in _model: - request_data["model"] = ( - f"projects/{vertex_project}/locations/{vertex_location}/publishers/google/models/{_model}" - ) + request_data[ + "model" + ] = f"projects/{vertex_project}/locations/{vertex_location}/publishers/google/models/{_model}" url = f"https://{vertex_location}-aiplatform.googleapis.com/v1beta1/projects/{vertex_project}/locations/{vertex_location}{request_route}" else: diff --git a/litellm/llms/vertex_ai/gemini/transformation.py b/litellm/llms/vertex_ai/gemini/transformation.py index d6bafc7c60..8067d51c87 100644 --- a/litellm/llms/vertex_ai/gemini/transformation.py +++ b/litellm/llms/vertex_ai/gemini/transformation.py @@ -27,6 +27,8 @@ from litellm.types.files import ( from litellm.types.llms.openai import ( AllMessageValues, ChatCompletionAssistantMessage, + ChatCompletionAudioObject, + ChatCompletionFileObject, ChatCompletionImageObject, ChatCompletionTextObject, ) @@ -85,7 +87,6 @@ def _process_gemini_image(image_url: str, format: Optional[str] = None) -> PartT and (image_type := format or _get_image_mime_type_from_url(image_url)) is not None ): - file_data = FileDataType(file_uri=image_url, mime_type=image_type) return PartType(file_data=file_data) elif "http://" in image_url or "https://" in image_url or "base64" in image_url: @@ -104,24 +105,53 @@ def _get_image_mime_type_from_url(url: str) -> Optional[str]: See gemini mime types: https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/image-understanding#image-requirements Supported by Gemini: - - PNG (`image/png`) - - JPEG (`image/jpeg`) - - WebP (`image/webp`) - Example: - url = https://example.com/image.jpg - Returns: image/jpeg + application/pdf + audio/mpeg + audio/mp3 + audio/wav + image/png + image/jpeg + image/webp + text/plain + video/mov + video/mpeg + video/mp4 + video/mpg + video/avi + video/wmv + video/mpegps + video/flv """ url = url.lower() - if url.endswith((".jpg", ".jpeg")): - return "image/jpeg" - elif url.endswith(".png"): - return "image/png" - elif url.endswith(".webp"): - return "image/webp" - elif url.endswith(".mp4"): - return "video/mp4" - elif url.endswith(".pdf"): - return "application/pdf" + + # Map file extensions to mime types + mime_types = { + # Images + (".jpg", ".jpeg"): "image/jpeg", + (".png",): "image/png", + (".webp",): "image/webp", + # Videos + (".mp4",): "video/mp4", + (".mov",): "video/mov", + (".mpeg", ".mpg"): "video/mpeg", + (".avi",): "video/avi", + (".wmv",): "video/wmv", + (".mpegps",): "video/mpegps", + (".flv",): "video/flv", + # Audio + (".mp3",): "audio/mp3", + (".wav",): "audio/wav", + (".mpeg",): "audio/mpeg", + # Documents + (".pdf",): "application/pdf", + (".txt",): "text/plain", + } + + # Check each extension group against the URL + for extensions, mime_type in mime_types.items(): + if any(url.endswith(ext) for ext in extensions): + return mime_type + return None @@ -153,7 +183,7 @@ def _gemini_convert_messages_with_history( # noqa: PLR0915 _message_content = messages[msg_i].get("content") if _message_content is not None and isinstance(_message_content, list): _parts: List[PartType] = [] - for element in _message_content: + for element_idx, element in enumerate(_message_content): if ( element["type"] == "text" and "text" in element @@ -175,6 +205,41 @@ def _gemini_convert_messages_with_history( # noqa: PLR0915 image_url=image_url, format=format ) _parts.append(_part) + elif element["type"] == "input_audio": + audio_element = cast(ChatCompletionAudioObject, element) + if audio_element["input_audio"].get("data") is not None: + _part = PartType( + inline_data=BlobType( + data=audio_element["input_audio"]["data"], + mime_type="audio/{}".format( + audio_element["input_audio"]["format"] + ), + ) + ) + _parts.append(_part) + elif element["type"] == "file": + file_element = cast(ChatCompletionFileObject, element) + file_id = file_element["file"].get("file_id") + format = file_element["file"].get("format") + + if not file_id: + continue + mime_type = format or _get_image_mime_type_from_url(file_id) + + if mime_type is not None: + _part = PartType( + file_data=FileDataType( + file_uri=file_id, + mime_type=mime_type, + ) + ) + _parts.append(_part) + else: + raise Exception( + "Unable to determine mime type for file_id: {}, set this explicitly using message[{}].content[{}].file.format".format( + file_id, msg_i, element_idx + ) + ) user_content.extend(_parts) elif ( _message_content is not None @@ -414,18 +479,19 @@ async def async_transform_request_body( context_caching_endpoints = ContextCachingEndpoints() if gemini_api_key is not None: - messages, cached_content = ( - await context_caching_endpoints.async_check_and_create_cache( - messages=messages, - api_key=gemini_api_key, - api_base=api_base, - model=model, - client=client, - timeout=timeout, - extra_headers=extra_headers, - cached_content=optional_params.pop("cached_content", None), - logging_obj=logging_obj, - ) + ( + messages, + cached_content, + ) = await context_caching_endpoints.async_check_and_create_cache( + messages=messages, + api_key=gemini_api_key, + api_base=api_base, + model=model, + client=client, + timeout=timeout, + extra_headers=extra_headers, + cached_content=optional_params.pop("cached_content", None), + logging_obj=logging_obj, ) else: # [TODO] implement context caching for gemini as well cached_content = optional_params.pop("cached_content", None) diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 73215b4048..860dec9eb2 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -207,7 +207,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "extra_headers", "seed", "logprobs", - "top_logprobs" # Added this to list of supported openAI params + "top_logprobs", # Added this to list of supported openAI params ] def map_tool_choice_values( @@ -246,9 +246,9 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): value = _remove_strict_from_schema(value) for tool in value: - openai_function_object: Optional[ChatCompletionToolParamFunctionChunk] = ( - None - ) + openai_function_object: Optional[ + ChatCompletionToolParamFunctionChunk + ] = None if "function" in tool: # tools list _openai_function_object = ChatCompletionToolParamFunctionChunk( # type: ignore **tool["function"] @@ -419,6 +419,49 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "europe-west9", ] + @staticmethod + def get_model_for_vertex_ai_url(model: str) -> str: + """ + Returns the model name to use in the request to Vertex AI + + Handles 2 cases: + 1. User passed `model="vertex_ai/gemini/ft-uuid"`, we need to return `ft-uuid` for the request to Vertex AI + 2. User passed `model="vertex_ai/gemini-2.0-flash-001"`, we need to return `gemini-2.0-flash-001` for the request to Vertex AI + + Args: + model (str): The model name to use in the request to Vertex AI + + Returns: + str: The model name to use in the request to Vertex AI + """ + if VertexGeminiConfig._is_model_gemini_spec_model(model): + return VertexGeminiConfig._get_model_name_from_gemini_spec_model(model) + return model + + @staticmethod + def _is_model_gemini_spec_model(model: Optional[str]) -> bool: + """ + Returns true if user is trying to call custom model in `/gemini` request/response format + """ + if model is None: + return False + if "gemini/" in model: + return True + return False + + @staticmethod + def _get_model_name_from_gemini_spec_model(model: str) -> str: + """ + Returns the model name if model="vertex_ai/gemini/" + + Example: + - model = "gemini/1234567890" + - returns "1234567890" + """ + if "gemini/" in model: + return model.split("/")[-1] + return model + def get_flagged_finish_reasons(self) -> Dict[str, str]: """ Return Dictionary of finish reasons which indicate response was flagged @@ -600,16 +643,25 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): completion_response: GenerateContentResponseBody, ) -> Usage: cached_tokens: Optional[int] = None + audio_tokens: Optional[int] = None + text_tokens: Optional[int] = None prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None if "cachedContentTokenCount" in completion_response["usageMetadata"]: cached_tokens = completion_response["usageMetadata"][ "cachedContentTokenCount" ] + if "promptTokensDetails" in completion_response["usageMetadata"]: + for detail in completion_response["usageMetadata"]["promptTokensDetails"]: + if detail["modality"] == "AUDIO": + audio_tokens = detail["tokenCount"] + elif detail["modality"] == "TEXT": + text_tokens = detail["tokenCount"] - if cached_tokens is not None: - prompt_tokens_details = PromptTokensDetailsWrapper( - cached_tokens=cached_tokens, - ) + prompt_tokens_details = PromptTokensDetailsWrapper( + cached_tokens=cached_tokens, + audio_tokens=audio_tokens, + text_tokens=text_tokens, + ) ## GET USAGE ## usage = Usage( prompt_tokens=completion_response["usageMetadata"].get( @@ -748,6 +800,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): model_response.choices.append(choice) usage = self._calculate_usage(completion_response=completion_response) + setattr(model_response, "usage", usage) ## ADD GROUNDING METADATA ## @@ -760,15 +813,15 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): ## ADD SAFETY RATINGS ## setattr(model_response, "vertex_ai_safety_results", safety_ratings) - model_response._hidden_params["vertex_ai_safety_results"] = ( - safety_ratings # older approach - maintaining to prevent regressions - ) + model_response._hidden_params[ + "vertex_ai_safety_results" + ] = safety_ratings # older approach - maintaining to prevent regressions ## ADD CITATION METADATA ## setattr(model_response, "vertex_ai_citation_metadata", citation_metadata) - model_response._hidden_params["vertex_ai_citation_metadata"] = ( - citation_metadata # older approach - maintaining to prevent regressions - ) + model_response._hidden_params[ + "vertex_ai_citation_metadata" + ] = citation_metadata # older approach - maintaining to prevent regressions except Exception as e: raise VertexAIError( diff --git a/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py b/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py index 0fe5145a14..ecfe2ee8b4 100644 --- a/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py +++ b/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_handler.py @@ -47,7 +47,6 @@ class GoogleBatchEmbeddings(VertexLLM): timeout=300, client=None, ) -> EmbeddingResponse: - _auth_header, vertex_project = self._ensure_access_token( credentials=vertex_credentials, project_id=vertex_project, diff --git a/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_transformation.py b/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_transformation.py index 592dac5846..2c0f5dad22 100644 --- a/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_transformation.py +++ b/litellm/llms/vertex_ai/gemini_embeddings/batch_embed_content_transformation.py @@ -52,7 +52,6 @@ def process_response( model: str, _predictions: VertexAIBatchEmbeddingsResponseObject, ) -> EmbeddingResponse: - openai_embeddings: List[Embedding] = [] for embedding in _predictions["embeddings"]: openai_embedding = Embedding( diff --git a/litellm/llms/vertex_ai/multimodal_embeddings/embedding_handler.py b/litellm/llms/vertex_ai/multimodal_embeddings/embedding_handler.py index cd3de6e526..88d7339449 100644 --- a/litellm/llms/vertex_ai/multimodal_embeddings/embedding_handler.py +++ b/litellm/llms/vertex_ai/multimodal_embeddings/embedding_handler.py @@ -1,5 +1,5 @@ import json -from typing import List, Literal, Optional, Union +from typing import Literal, Optional, Union import httpx @@ -14,15 +14,11 @@ from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( VertexAIError, VertexLLM, ) -from litellm.types.llms.vertex_ai import ( - Instance, - InstanceImage, - InstanceVideo, - MultimodalPredictions, - VertexMultimodalEmbeddingRequest, -) -from litellm.types.utils import Embedding, EmbeddingResponse -from litellm.utils import is_base64_encoded +from litellm.types.utils import EmbeddingResponse + +from .transformation import VertexAIMultimodalEmbeddingConfig + +vertex_multimodal_embedding_handler = VertexAIMultimodalEmbeddingConfig() class VertexMultimodalEmbedding(VertexLLM): @@ -41,9 +37,11 @@ class VertexMultimodalEmbedding(VertexLLM): model_response: EmbeddingResponse, custom_llm_provider: Literal["gemini", "vertex_ai"], optional_params: dict, + litellm_params: dict, logging_obj: LiteLLMLoggingObj, api_key: Optional[str] = None, api_base: Optional[str] = None, + headers: dict = {}, encoding=None, vertex_project=None, vertex_location=None, @@ -52,7 +50,6 @@ class VertexMultimodalEmbedding(VertexLLM): timeout=300, client=None, ) -> EmbeddingResponse: - _auth_header, vertex_project = self._ensure_access_token( credentials=vertex_credentials, project_id=vertex_project, @@ -86,31 +83,18 @@ class VertexMultimodalEmbedding(VertexLLM): else: sync_handler = client # type: ignore - optional_params = optional_params or {} + request_data = vertex_multimodal_embedding_handler.transform_embedding_request( + model, input, optional_params, headers + ) - request_data = VertexMultimodalEmbeddingRequest() - - if "instances" in optional_params: - request_data["instances"] = optional_params["instances"] - elif isinstance(input, list): - vertex_instances: List[Instance] = self.process_openai_embedding_input( - _input=input - ) - request_data["instances"] = vertex_instances - - else: - # construct instances - vertex_request_instance = Instance(**optional_params) - - if isinstance(input, str): - vertex_request_instance = self._process_input_element(input) - - request_data["instances"] = [vertex_request_instance] - - headers = { - "Content-Type": "application/json; charset=utf-8", - "Authorization": f"Bearer {auth_header}", - } + headers = vertex_multimodal_embedding_handler.validate_environment( + headers=headers, + model=model, + messages=[], + optional_params=optional_params, + api_key=auth_header, + api_base=api_base, + ) ## LOGGING logging_obj.pre_call( @@ -132,6 +116,10 @@ class VertexMultimodalEmbedding(VertexLLM): headers=headers, client=client, model_response=model_response, + optional_params=optional_params, + litellm_params=litellm_params, + logging_obj=logging_obj, + api_key=api_key, ) response = sync_handler.post( @@ -140,34 +128,30 @@ class VertexMultimodalEmbedding(VertexLLM): data=json.dumps(request_data), ) - if response.status_code != 200: - raise Exception(f"Error: {response.status_code} {response.text}") - - _json_response = response.json() - if "predictions" not in _json_response: - raise litellm.InternalServerError( - message=f"embedding response does not contain 'predictions', got {_json_response}", - llm_provider="vertex_ai", - model=model, - ) - _predictions = _json_response["predictions"] - vertex_predictions = MultimodalPredictions(predictions=_predictions) - model_response.data = self.transform_embedding_response_to_openai( - predictions=vertex_predictions + return vertex_multimodal_embedding_handler.transform_embedding_response( + model=model, + raw_response=response, + model_response=model_response, + logging_obj=logging_obj, + api_key=api_key, + request_data=request_data, + optional_params=optional_params, + litellm_params=litellm_params, ) - model_response.model = model - - return model_response async def async_multimodal_embedding( self, model: str, api_base: str, - data: VertexMultimodalEmbeddingRequest, + optional_params: dict, + litellm_params: dict, + data: dict, model_response: litellm.EmbeddingResponse, timeout: Optional[Union[float, httpx.Timeout]], + logging_obj: LiteLLMLoggingObj, headers={}, client: Optional[AsyncHTTPHandler] = None, + api_key: Optional[str] = None, ) -> litellm.EmbeddingResponse: if client is None: _params = {} @@ -191,112 +175,13 @@ class VertexMultimodalEmbedding(VertexLLM): except httpx.TimeoutException: raise VertexAIError(status_code=408, message="Timeout error occurred.") - _json_response = response.json() - if "predictions" not in _json_response: - raise litellm.InternalServerError( - message=f"embedding response does not contain 'predictions', got {_json_response}", - llm_provider="vertex_ai", - model=model, - ) - _predictions = _json_response["predictions"] - - vertex_predictions = MultimodalPredictions(predictions=_predictions) - model_response.data = self.transform_embedding_response_to_openai( - predictions=vertex_predictions + return vertex_multimodal_embedding_handler.transform_embedding_response( + model=model, + raw_response=response, + model_response=model_response, + logging_obj=logging_obj, + api_key=api_key, + request_data=data, + optional_params=optional_params, + litellm_params=litellm_params, ) - model_response.model = model - - return model_response - - def _process_input_element(self, input_element: str) -> Instance: - """ - Process the input element for multimodal embedding requests. checks if the if the input is gcs uri, base64 encoded image or plain text. - - Args: - input_element (str): The input element to process. - - Returns: - Dict[str, Any]: A dictionary representing the processed input element. - """ - if len(input_element) == 0: - return Instance(text=input_element) - elif "gs://" in input_element: - if "mp4" in input_element: - return Instance(video=InstanceVideo(gcsUri=input_element)) - else: - return Instance(image=InstanceImage(gcsUri=input_element)) - elif is_base64_encoded(s=input_element): - return Instance( - image=InstanceImage( - bytesBase64Encoded=( - input_element.split(",")[1] - if "," in input_element - else input_element - ) - ) - ) - else: - return Instance(text=input_element) - - def process_openai_embedding_input( - self, _input: Union[list, str] - ) -> List[Instance]: - """ - Process the input for multimodal embedding requests. - - Args: - _input (Union[list, str]): The input data to process. - - Returns: - List[Instance]: A list of processed VertexAI Instance objects. - """ - - _input_list = None - if not isinstance(_input, list): - _input_list = [_input] - else: - _input_list = _input - - processed_instances = [] - for element in _input_list: - if isinstance(element, str): - instance = Instance(**self._process_input_element(element)) - elif isinstance(element, dict): - instance = Instance(**element) - else: - raise ValueError(f"Unsupported input type: {type(element)}") - processed_instances.append(instance) - - return processed_instances - - def transform_embedding_response_to_openai( - self, predictions: MultimodalPredictions - ) -> List[Embedding]: - - openai_embeddings: List[Embedding] = [] - if "predictions" in predictions: - for idx, _prediction in enumerate(predictions["predictions"]): - if _prediction: - if "textEmbedding" in _prediction: - openai_embedding_object = Embedding( - embedding=_prediction["textEmbedding"], - index=idx, - object="embedding", - ) - openai_embeddings.append(openai_embedding_object) - elif "imageEmbedding" in _prediction: - openai_embedding_object = Embedding( - embedding=_prediction["imageEmbedding"], - index=idx, - object="embedding", - ) - openai_embeddings.append(openai_embedding_object) - elif "videoEmbeddings" in _prediction: - for video_embedding in _prediction["videoEmbeddings"]: - openai_embedding_object = Embedding( - embedding=video_embedding["embedding"], - index=idx, - object="embedding", - ) - openai_embeddings.append(openai_embedding_object) - return openai_embeddings diff --git a/litellm/llms/vertex_ai/multimodal_embeddings/transformation.py b/litellm/llms/vertex_ai/multimodal_embeddings/transformation.py new file mode 100644 index 0000000000..afa58c7e5c --- /dev/null +++ b/litellm/llms/vertex_ai/multimodal_embeddings/transformation.py @@ -0,0 +1,296 @@ +from typing import List, Optional, Union, cast + +from httpx import Headers, Response + +from litellm.exceptions import InternalServerError +from litellm.llms.base_llm.chat.transformation import BaseLLMException +from litellm.llms.base_llm.embedding.transformation import LiteLLMLoggingObj +from litellm.types.llms.openai import AllEmbeddingInputValues, AllMessageValues +from litellm.types.llms.vertex_ai import ( + Instance, + InstanceImage, + InstanceVideo, + MultimodalPredictions, + VertexMultimodalEmbeddingRequest, +) +from litellm.types.utils import ( + Embedding, + EmbeddingResponse, + PromptTokensDetailsWrapper, + Usage, +) +from litellm.utils import _count_characters, is_base64_encoded + +from ...base_llm.embedding.transformation import BaseEmbeddingConfig +from ..common_utils import VertexAIError + + +class VertexAIMultimodalEmbeddingConfig(BaseEmbeddingConfig): + def get_supported_openai_params(self, model: str) -> list: + return ["dimensions"] + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + model: str, + drop_params: bool, + ) -> dict: + for param, value in non_default_params.items(): + if param == "dimensions": + optional_params["outputDimensionality"] = value + return optional_params + + def validate_environment( + self, + headers: dict, + model: str, + messages: List[AllMessageValues], + optional_params: dict, + api_key: Optional[str] = None, + api_base: Optional[str] = None, + ) -> dict: + default_headers = { + "Content-Type": "application/json; charset=utf-8", + "Authorization": f"Bearer {api_key}", + } + headers.update(default_headers) + return headers + + def _process_input_element(self, input_element: str) -> Instance: + """ + Process the input element for multimodal embedding requests. checks if the if the input is gcs uri, base64 encoded image or plain text. + + Args: + input_element (str): The input element to process. + + Returns: + Dict[str, Any]: A dictionary representing the processed input element. + """ + if len(input_element) == 0: + return Instance(text=input_element) + elif "gs://" in input_element: + if "mp4" in input_element: + return Instance(video=InstanceVideo(gcsUri=input_element)) + else: + return Instance(image=InstanceImage(gcsUri=input_element)) + elif is_base64_encoded(s=input_element): + return Instance( + image=InstanceImage( + bytesBase64Encoded=( + input_element.split(",")[1] + if "," in input_element + else input_element + ) + ) + ) + else: + return Instance(text=input_element) + + def process_openai_embedding_input( + self, _input: Union[list, str] + ) -> List[Instance]: + """ + Process the input for multimodal embedding requests. + + Args: + _input (Union[list, str]): The input data to process. + + Returns: + Union[Instance, List[Instance]]: Either a single Instance or list of Instance objects. + """ + _input_list = [_input] if not isinstance(_input, list) else _input + processed_instances = [] + + i = 0 + while i < len(_input_list): + current = _input_list[i] + + # Look ahead for potential media elements + next_elem = _input_list[i + 1] if i + 1 < len(_input_list) else None + + # If current is a text and next is a GCS URI, or current is a GCS URI + if isinstance(current, str): + instance_args: Instance = {} + + # Process current element + if "gs://" not in current: + instance_args["text"] = current + elif "mp4" in current: + instance_args["video"] = InstanceVideo(gcsUri=current) + else: + instance_args["image"] = InstanceImage(gcsUri=current) + + # Check next element if it's a GCS URI + if next_elem and isinstance(next_elem, str) and "gs://" in next_elem: + if "mp4" in next_elem: + instance_args["video"] = InstanceVideo(gcsUri=next_elem) + else: + instance_args["image"] = InstanceImage(gcsUri=next_elem) + i += 2 # Skip next element since we processed it + else: + i += 1 # Move to next element + + processed_instances.append(instance_args) + continue + + # Handle dict or other types + if isinstance(current, dict): + instance = Instance(**current) + processed_instances.append(instance) + else: + raise ValueError(f"Unsupported input type: {type(current)}") + i += 1 + + return processed_instances + + def transform_embedding_request( + self, + model: str, + input: AllEmbeddingInputValues, + optional_params: dict, + headers: dict, + ) -> dict: + optional_params = optional_params or {} + + request_data = VertexMultimodalEmbeddingRequest(instances=[]) + + if "instances" in optional_params: + request_data["instances"] = optional_params["instances"] + elif isinstance(input, list): + vertex_instances: List[Instance] = self.process_openai_embedding_input( + _input=input + ) + request_data["instances"] = vertex_instances + + else: + # construct instances + vertex_request_instance = Instance(**optional_params) + + if isinstance(input, str): + vertex_request_instance = self._process_input_element(input) + + request_data["instances"] = [vertex_request_instance] + + return cast(dict, request_data) + + def transform_embedding_response( + self, + model: str, + raw_response: Response, + model_response: EmbeddingResponse, + logging_obj: LiteLLMLoggingObj, + api_key: Optional[str], + request_data: dict, + optional_params: dict, + litellm_params: dict, + ) -> EmbeddingResponse: + if raw_response.status_code != 200: + raise Exception(f"Error: {raw_response.status_code} {raw_response.text}") + + _json_response = raw_response.json() + if "predictions" not in _json_response: + raise InternalServerError( + message=f"embedding response does not contain 'predictions', got {_json_response}", + llm_provider="vertex_ai", + model=model, + ) + _predictions = _json_response["predictions"] + vertex_predictions = MultimodalPredictions(predictions=_predictions) + model_response.data = self.transform_embedding_response_to_openai( + predictions=vertex_predictions + ) + model_response.model = model + + model_response.usage = self.calculate_usage( + request_data=cast(VertexMultimodalEmbeddingRequest, request_data), + vertex_predictions=vertex_predictions, + ) + + return model_response + + def calculate_usage( + self, + request_data: VertexMultimodalEmbeddingRequest, + vertex_predictions: MultimodalPredictions, + ) -> Usage: + ## Calculate text embeddings usage + prompt: Optional[str] = None + character_count: Optional[int] = None + + for instance in request_data["instances"]: + text = instance.get("text") + if text: + if prompt is None: + prompt = text + else: + prompt += text + + if prompt is not None: + character_count = _count_characters(prompt) + + ## Calculate image embeddings usage + image_count = 0 + for instance in request_data["instances"]: + if instance.get("image"): + image_count += 1 + + ## Calculate video embeddings usage + video_length_seconds = 0 + for prediction in vertex_predictions["predictions"]: + video_embeddings = prediction.get("videoEmbeddings") + if video_embeddings: + for embedding in video_embeddings: + duration = embedding["endOffsetSec"] - embedding["startOffsetSec"] + video_length_seconds += duration + + prompt_tokens_details = PromptTokensDetailsWrapper( + character_count=character_count, + image_count=image_count, + video_length_seconds=video_length_seconds, + ) + + return Usage( + prompt_tokens=0, + completion_tokens=0, + total_tokens=0, + prompt_tokens_details=prompt_tokens_details, + ) + + def transform_embedding_response_to_openai( + self, predictions: MultimodalPredictions + ) -> List[Embedding]: + openai_embeddings: List[Embedding] = [] + if "predictions" in predictions: + for idx, _prediction in enumerate(predictions["predictions"]): + if _prediction: + if "textEmbedding" in _prediction: + openai_embedding_object = Embedding( + embedding=_prediction["textEmbedding"], + index=idx, + object="embedding", + ) + openai_embeddings.append(openai_embedding_object) + elif "imageEmbedding" in _prediction: + openai_embedding_object = Embedding( + embedding=_prediction["imageEmbedding"], + index=idx, + object="embedding", + ) + openai_embeddings.append(openai_embedding_object) + elif "videoEmbeddings" in _prediction: + for video_embedding in _prediction["videoEmbeddings"]: + openai_embedding_object = Embedding( + embedding=video_embedding["embedding"], + index=idx, + object="embedding", + ) + openai_embeddings.append(openai_embedding_object) + return openai_embeddings + + def get_error_class( + self, error_message: str, status_code: int, headers: Union[dict, Headers] + ) -> BaseLLMException: + return VertexAIError( + status_code=status_code, message=error_message, headers=headers + ) diff --git a/litellm/llms/vertex_ai/vertex_ai_non_gemini.py b/litellm/llms/vertex_ai/vertex_ai_non_gemini.py index 744e1eb317..df267d9623 100644 --- a/litellm/llms/vertex_ai/vertex_ai_non_gemini.py +++ b/litellm/llms/vertex_ai/vertex_ai_non_gemini.py @@ -323,7 +323,6 @@ def completion( # noqa: PLR0915 ) completion_response = chat.send_message(prompt, **optional_params).text elif mode == "text": - if fake_stream is not True and stream is True: request_str += ( f"llm_model.predict_streaming({prompt}, **{optional_params})\n" @@ -506,7 +505,6 @@ async def async_completion( # noqa: PLR0915 Add support for acompletion calls for gemini-pro """ try: - response_obj = None completion_response = None if mode == "chat": diff --git a/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py b/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py index fb2393631b..b8d2658f80 100644 --- a/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py +++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py @@ -110,7 +110,6 @@ class VertexAIPartnerModels(VertexBase): message="""Upgrade vertex ai. Run `pip install "google-cloud-aiplatform>=1.38"`""", ) try: - vertex_httpx_logic = VertexLLM() access_token, project_id = vertex_httpx_logic._ensure_access_token( diff --git a/litellm/llms/vertex_ai/vertex_embeddings/embedding_handler.py b/litellm/llms/vertex_ai/vertex_embeddings/embedding_handler.py index 2e8051d4d2..1167ca285f 100644 --- a/litellm/llms/vertex_ai/vertex_embeddings/embedding_handler.py +++ b/litellm/llms/vertex_ai/vertex_embeddings/embedding_handler.py @@ -86,10 +86,8 @@ class VertexEmbedding(VertexBase): mode="embedding", ) headers = self.set_headers(auth_header=auth_header, extra_headers=extra_headers) - vertex_request: VertexEmbeddingRequest = ( - litellm.vertexAITextEmbeddingConfig.transform_openai_request_to_vertex_embedding_request( - input=input, optional_params=optional_params, model=model - ) + vertex_request: VertexEmbeddingRequest = litellm.vertexAITextEmbeddingConfig.transform_openai_request_to_vertex_embedding_request( + input=input, optional_params=optional_params, model=model ) _client_params = {} @@ -178,10 +176,8 @@ class VertexEmbedding(VertexBase): mode="embedding", ) headers = self.set_headers(auth_header=auth_header, extra_headers=extra_headers) - vertex_request: VertexEmbeddingRequest = ( - litellm.vertexAITextEmbeddingConfig.transform_openai_request_to_vertex_embedding_request( - input=input, optional_params=optional_params, model=model - ) + vertex_request: VertexEmbeddingRequest = litellm.vertexAITextEmbeddingConfig.transform_openai_request_to_vertex_embedding_request( + input=input, optional_params=optional_params, model=model ) _async_client_params = {} diff --git a/litellm/llms/vertex_ai/vertex_embeddings/transformation.py b/litellm/llms/vertex_ai/vertex_embeddings/transformation.py index d9e84fca03..97af558041 100644 --- a/litellm/llms/vertex_ai/vertex_embeddings/transformation.py +++ b/litellm/llms/vertex_ai/vertex_embeddings/transformation.py @@ -212,7 +212,6 @@ class VertexAITextEmbeddingConfig(BaseModel): embedding_response = [] input_tokens: int = 0 for idx, element in enumerate(_predictions): - embedding = element["embeddings"] embedding_response.append( { diff --git a/litellm/llms/vertex_ai/vertex_model_garden/main.py b/litellm/llms/vertex_ai/vertex_model_garden/main.py index 7b54d4e34b..1c57096734 100644 --- a/litellm/llms/vertex_ai/vertex_model_garden/main.py +++ b/litellm/llms/vertex_ai/vertex_model_garden/main.py @@ -76,7 +76,6 @@ class VertexAIModelGardenModels(VertexBase): VertexLLM, ) except Exception as e: - raise VertexAIError( status_code=400, message=f"""vertexai import failed please run `pip install -U "google-cloud-aiplatform>=1.38"`. Got error: {e}""", diff --git a/litellm/llms/watsonx/chat/transformation.py b/litellm/llms/watsonx/chat/transformation.py index f253da6f5b..2ff1dd6a68 100644 --- a/litellm/llms/watsonx/chat/transformation.py +++ b/litellm/llms/watsonx/chat/transformation.py @@ -15,7 +15,6 @@ from ..common_utils import IBMWatsonXMixin class IBMWatsonXChatConfig(IBMWatsonXMixin, OpenAIGPTConfig): - def get_supported_openai_params(self, model: str) -> List: return [ "temperature", # equivalent to temperature diff --git a/litellm/llms/xai/common_utils.py b/litellm/llms/xai/common_utils.py new file mode 100644 index 0000000000..d7ceeadd95 --- /dev/null +++ b/litellm/llms/xai/common_utils.py @@ -0,0 +1,51 @@ +from typing import List, Optional + +import httpx + +import litellm +from litellm.llms.base_llm.base_utils import BaseLLMModelInfo +from litellm.secret_managers.main import get_secret_str + + +class XAIModelInfo(BaseLLMModelInfo): + @staticmethod + def get_api_base(api_base: Optional[str] = None) -> Optional[str]: + return api_base or get_secret_str("XAI_API_BASE") or "https://api.x.ai" + + @staticmethod + def get_api_key(api_key: Optional[str] = None) -> Optional[str]: + return api_key or get_secret_str("XAI_API_KEY") + + @staticmethod + def get_base_model(model: str) -> Optional[str]: + return model.replace("xai/", "") + + def get_models( + self, api_key: Optional[str] = None, api_base: Optional[str] = None + ) -> List[str]: + api_base = self.get_api_base(api_base) + api_key = self.get_api_key(api_key) + if api_base is None or api_key is None: + raise ValueError( + "XAI_API_BASE or XAI_API_KEY is not set. Please set the environment variable, to query XAI's `/models` endpoint." + ) + response = litellm.module_level_client.get( + url=f"{api_base}/v1/models", + headers={"Authorization": f"Bearer {api_key}"}, + ) + + try: + response.raise_for_status() + except httpx.HTTPStatusError: + raise Exception( + f"Failed to fetch models from XAI. Status code: {response.status_code}, Response: {response.text}" + ) + + models = response.json()["data"] + + litellm_model_names = [] + for model in models: + stripped_model_name = model["id"] + litellm_model_name = "xai/" + stripped_model_name + litellm_model_names.append(litellm_model_name) + return litellm_model_names diff --git a/litellm/main.py b/litellm/main.py index 94e19aab0c..f69454aaad 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -946,14 +946,16 @@ def completion( # type: ignore # noqa: PLR0915 ## PROMPT MANAGEMENT HOOKS ## if isinstance(litellm_logging_obj, LiteLLMLoggingObj) and prompt_id is not None: - model, messages, optional_params = ( - litellm_logging_obj.get_chat_completion_prompt( - model=model, - messages=messages, - non_default_params=non_default_params, - prompt_id=prompt_id, - prompt_variables=prompt_variables, - ) + ( + model, + messages, + optional_params, + ) = litellm_logging_obj.get_chat_completion_prompt( + model=model, + messages=messages, + non_default_params=non_default_params, + prompt_id=prompt_id, + prompt_variables=prompt_variables, ) try: @@ -1246,7 +1248,6 @@ def completion( # type: ignore # noqa: PLR0915 optional_params["max_retries"] = max_retries if litellm.AzureOpenAIO1Config().is_o_series_model(model=model): - ## LOAD CONFIG - if set config = litellm.AzureOpenAIO1Config.get_config() for k, v in config.items(): @@ -2654,9 +2655,9 @@ def completion( # type: ignore # noqa: PLR0915 "aws_region_name" not in optional_params or optional_params["aws_region_name"] is None ): - optional_params["aws_region_name"] = ( - aws_bedrock_client.meta.region_name - ) + optional_params[ + "aws_region_name" + ] = aws_bedrock_client.meta.region_name bedrock_route = BedrockModelInfo.get_bedrock_route(model) if bedrock_route == "converse": @@ -3723,6 +3724,7 @@ def embedding( # noqa: PLR0915 encoding=encoding, logging_obj=logging, optional_params=optional_params, + litellm_params=litellm_params_dict, model_response=EmbeddingResponse(), vertex_project=vertex_ai_project, vertex_location=vertex_ai_location, @@ -4361,9 +4363,9 @@ def adapter_completion( new_kwargs = translation_obj.translate_completion_input_params(kwargs=kwargs) response: Union[ModelResponse, CustomStreamWrapper] = completion(**new_kwargs) # type: ignore - translated_response: Optional[Union[BaseModel, AdapterCompletionStreamWrapper]] = ( - None - ) + translated_response: Optional[ + Union[BaseModel, AdapterCompletionStreamWrapper] + ] = None if isinstance(response, ModelResponse): translated_response = translation_obj.translate_completion_output_params( response=response @@ -4435,13 +4437,16 @@ async def amoderation( optional_params = GenericLiteLLMParams(**kwargs) try: - model, _custom_llm_provider, _dynamic_api_key, _dynamic_api_base = ( - litellm.get_llm_provider( - model=model or "", - custom_llm_provider=custom_llm_provider, - api_base=optional_params.api_base, - api_key=optional_params.api_key, - ) + ( + model, + _custom_llm_provider, + _dynamic_api_key, + _dynamic_api_base, + ) = litellm.get_llm_provider( + model=model or "", + custom_llm_provider=custom_llm_provider, + api_base=optional_params.api_base, + api_key=optional_params.api_key, ) except litellm.BadRequestError: # `model` is optional field for moderation - get_llm_provider will throw BadRequestError if model is not set / not recognized @@ -5095,6 +5100,12 @@ def transcription( response: Optional[ Union[TranscriptionResponse, Coroutine[Any, Any, TranscriptionResponse]] ] = None + + provider_config = ProviderConfigManager.get_provider_audio_transcription_config( + model=model, + provider=LlmProviders(custom_llm_provider), + ) + if custom_llm_provider == "azure": # azure configs api_base = api_base or litellm.api_base or get_secret_str("AZURE_API_BASE") @@ -5161,12 +5172,15 @@ def transcription( max_retries=max_retries, api_base=api_base, api_key=api_key, + provider_config=provider_config, + litellm_params=litellm_params_dict, ) elif custom_llm_provider == "deepgram": response = base_llm_http_handler.audio_transcriptions( model=model, audio_file=file, optional_params=optional_params, + litellm_params=litellm_params_dict, model_response=model_response, atranscription=atranscription, client=( @@ -5185,6 +5199,7 @@ def transcription( api_key=api_key, custom_llm_provider="deepgram", headers={}, + provider_config=provider_config, ) if response is None: raise ValueError("Unmapped provider passed in. Unable to get the response.") @@ -5394,7 +5409,6 @@ def speech( # noqa: PLR0915 litellm_params=litellm_params_dict, ) elif custom_llm_provider == "vertex_ai" or custom_llm_provider == "vertex_ai_beta": - generic_optional_params = GenericLiteLLMParams(**kwargs) api_base = generic_optional_params.api_base or "" @@ -5449,7 +5463,6 @@ def speech( # noqa: PLR0915 async def ahealth_check_wildcard_models( model: str, custom_llm_provider: str, model_params: dict ) -> dict: - # this is a wildcard model, we need to pick a random model from the provider cheapest_models = pick_cheapest_chat_models_from_llm_provider( custom_llm_provider=custom_llm_provider, n=3 @@ -5772,9 +5785,9 @@ def stream_chunk_builder( # noqa: PLR0915 ] if len(content_chunks) > 0: - response["choices"][0]["message"]["content"] = ( - processor.get_combined_content(content_chunks) - ) + response["choices"][0]["message"][ + "content" + ] = processor.get_combined_content(content_chunks) audio_chunks = [ chunk diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 06f7238c01..64525d660b 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -1176,21 +1176,40 @@ "output_cost_per_pixel": 0.0, "litellm_provider": "openai" }, + "gpt-4o-transcribe": { + "mode": "audio_transcription", + "input_cost_per_token": 0.0000025, + "input_cost_per_audio_token": 0.000006, + "output_cost_per_token": 0.00001, + "litellm_provider": "openai", + "supported_endpoints": ["/v1/audio/transcriptions"] + }, + "gpt-4o-mini-transcribe": { + "mode": "audio_transcription", + "input_cost_per_token": 0.00000125, + "input_cost_per_audio_token": 0.000003, + "output_cost_per_token": 0.000005, + "litellm_provider": "openai", + "supported_endpoints": ["/v1/audio/transcriptions"] + }, "whisper-1": { "mode": "audio_transcription", "input_cost_per_second": 0.0001, "output_cost_per_second": 0.0001, - "litellm_provider": "openai" + "litellm_provider": "openai", + "supported_endpoints": ["/v1/audio/transcriptions"] }, "tts-1": { "mode": "audio_speech", "input_cost_per_character": 0.000015, - "litellm_provider": "openai" + "litellm_provider": "openai", + "supported_endpoints": ["/v1/audio/speech"] }, "tts-1-hd": { "mode": "audio_speech", "input_cost_per_character": 0.000030, - "litellm_provider": "openai" + "litellm_provider": "openai", + "supported_endpoints": ["/v1/audio/speech"] }, "azure/gpt-4o-mini-realtime-preview-2024-12-17": { "max_tokens": 4096, @@ -4677,6 +4696,8 @@ "supports_vision": true, "supports_response_schema": true, "supports_audio_output": true, + "supports_audio_input": true, + "supported_modalities": ["text", "image", "audio", "video"], "supports_tool_choice": true, "source": "https://ai.google.dev/pricing#2_0flash" }, @@ -5373,6 +5394,40 @@ "mode": "embedding", "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models" }, + "multimodalembedding": { + "max_tokens": 2048, + "max_input_tokens": 2048, + "output_vector_size": 768, + "input_cost_per_character": 0.0000002, + "input_cost_per_image": 0.0001, + "input_cost_per_video_per_second": 0.0005, + "input_cost_per_video_per_second_above_8s_interval": 0.0010, + "input_cost_per_video_per_second_above_15s_interval": 0.0020, + "input_cost_per_token": 0.0000008, + "output_cost_per_token": 0, + "litellm_provider": "vertex_ai-embedding-models", + "mode": "embedding", + "supported_endpoints": ["/v1/embeddings"], + "supported_modalities": ["text", "image", "video"], + "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models" + }, + "multimodalembedding@001": { + "max_tokens": 2048, + "max_input_tokens": 2048, + "output_vector_size": 768, + "input_cost_per_character": 0.0000002, + "input_cost_per_image": 0.0001, + "input_cost_per_video_per_second": 0.0005, + "input_cost_per_video_per_second_above_8s_interval": 0.0010, + "input_cost_per_video_per_second_above_15s_interval": 0.0020, + "input_cost_per_token": 0.0000008, + "output_cost_per_token": 0, + "litellm_provider": "vertex_ai-embedding-models", + "mode": "embedding", + "supported_endpoints": ["/v1/embeddings"], + "supported_modalities": ["text", "image", "video"], + "source": "https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models" + }, "text-embedding-large-exp-03-07": { "max_tokens": 8192, "max_input_tokens": 8192, diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py new file mode 100644 index 0000000000..df9ae0ea57 --- /dev/null +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -0,0 +1,153 @@ +""" +MCP Client Manager + +This class is responsible for managing MCP SSE clients. + +This is a Proxy +""" + +import asyncio +import json +from typing import Any, Dict, List, Optional + +from mcp import ClientSession +from mcp.client.sse import sse_client +from mcp.types import Tool as MCPTool + +from litellm._logging import verbose_logger +from litellm.types.mcp_server.mcp_server_manager import MCPInfo, MCPSSEServer + + +class MCPServerManager: + def __init__(self): + self.mcp_servers: List[MCPSSEServer] = [] + """ + eg. + [ + { + "name": "zapier_mcp_server", + "url": "https://actions.zapier.com/mcp/sk-ak-2ew3bofIeQIkNoeKIdXrF1Hhhp/sse" + }, + { + "name": "google_drive_mcp_server", + "url": "https://actions.zapier.com/mcp/sk-ak-2ew3bofIeQIkNoeKIdXrF1Hhhp/sse" + } + ] + """ + + self.tool_name_to_mcp_server_name_mapping: Dict[str, str] = {} + """ + { + "gmail_send_email": "zapier_mcp_server", + } + """ + + def load_servers_from_config(self, mcp_servers_config: Dict[str, Any]): + """ + Load the MCP Servers from the config + """ + for server_name, server_config in mcp_servers_config.items(): + _mcp_info: dict = server_config.get("mcp_info", None) or {} + mcp_info = MCPInfo(**_mcp_info) + mcp_info["server_name"] = server_name + self.mcp_servers.append( + MCPSSEServer( + name=server_name, + url=server_config["url"], + mcp_info=mcp_info, + ) + ) + verbose_logger.debug( + f"Loaded MCP Servers: {json.dumps(self.mcp_servers, indent=4, default=str)}" + ) + + self.initialize_tool_name_to_mcp_server_name_mapping() + + async def list_tools(self) -> List[MCPTool]: + """ + List all tools available across all MCP Servers. + + Returns: + List[MCPTool]: Combined list of tools from all servers + """ + list_tools_result: List[MCPTool] = [] + verbose_logger.debug("SSE SERVER MANAGER LISTING TOOLS") + + for server in self.mcp_servers: + tools = await self._get_tools_from_server(server) + list_tools_result.extend(tools) + + return list_tools_result + + async def _get_tools_from_server(self, server: MCPSSEServer) -> List[MCPTool]: + """ + Helper method to get tools from a single MCP server. + + Args: + server (MCPSSEServer): The server to query tools from + + Returns: + List[MCPTool]: List of tools available on the server + """ + verbose_logger.debug(f"Connecting to url: {server.url}") + + async with sse_client(url=server.url) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + + tools_result = await session.list_tools() + verbose_logger.debug(f"Tools from {server.name}: {tools_result}") + + # Update tool to server mapping + for tool in tools_result.tools: + self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name + + return tools_result.tools + + def initialize_tool_name_to_mcp_server_name_mapping(self): + """ + On startup, initialize the tool name to MCP server name mapping + """ + try: + if asyncio.get_running_loop(): + asyncio.create_task( + self._initialize_tool_name_to_mcp_server_name_mapping() + ) + except RuntimeError as e: # no running event loop + verbose_logger.exception( + f"No running event loop - skipping tool name to MCP server name mapping initialization: {str(e)}" + ) + + async def _initialize_tool_name_to_mcp_server_name_mapping(self): + """ + Call list_tools for each server and update the tool name to MCP server name mapping + """ + for server in self.mcp_servers: + tools = await self._get_tools_from_server(server) + for tool in tools: + self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name + + async def call_tool(self, name: str, arguments: Dict[str, Any]): + """ + Call a tool with the given name and arguments + """ + mcp_server = self._get_mcp_server_from_tool_name(name) + if mcp_server is None: + raise ValueError(f"Tool {name} not found") + async with sse_client(url=mcp_server.url) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + return await session.call_tool(name, arguments) + + def _get_mcp_server_from_tool_name(self, tool_name: str) -> Optional[MCPSSEServer]: + """ + Get the MCP Server from the tool name + """ + if tool_name in self.tool_name_to_mcp_server_name_mapping: + for server in self.mcp_servers: + if server.name == self.tool_name_to_mcp_server_name_mapping[tool_name]: + return server + return None + + +global_mcp_server_manager: MCPServerManager = MCPServerManager() diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py index f617312d5a..fe1eccb048 100644 --- a/litellm/proxy/_experimental/mcp_server/server.py +++ b/litellm/proxy/_experimental/mcp_server/server.py @@ -3,14 +3,21 @@ LiteLLM MCP Server Routes """ import asyncio -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Optional, Union from anyio import BrokenResourceError -from fastapi import APIRouter, HTTPException, Request +from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import StreamingResponse -from pydantic import ValidationError +from pydantic import ConfigDict, ValidationError from litellm._logging import verbose_logger +from litellm.constants import MCP_TOOL_NAME_PREFIX +from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj +from litellm.proxy._types import UserAPIKeyAuth +from litellm.proxy.auth.user_api_key_auth import user_api_key_auth +from litellm.types.mcp_server.mcp_server_manager import MCPInfo +from litellm.types.utils import StandardLoggingMCPToolCall +from litellm.utils import client # Check if MCP is available # "mcp" requires python 3.10 or higher, but several litellm users use python 3.8 @@ -36,9 +43,23 @@ if MCP_AVAILABLE: from mcp.types import TextContent as MCPTextContent from mcp.types import Tool as MCPTool + from .mcp_server_manager import global_mcp_server_manager from .sse_transport import SseServerTransport from .tool_registry import global_mcp_tool_registry + ###################################################### + ############ MCP Tools List REST API Response Object # + # Defined here because we don't want to add `mcp` as a + # required dependency for `litellm` pip package + ###################################################### + class ListMCPToolsRestAPIResponseObject(MCPTool): + """ + Object returned by the /tools/list REST API route. + """ + + mcp_info: Optional[MCPInfo] = None + model_config = ConfigDict(arbitrary_types_allowed=True) + ######################################################## ############ Initialize the MCP Server ################# ######################################################## @@ -52,9 +73,14 @@ if MCP_AVAILABLE: ######################################################## ############### MCP Server Routes ####################### ######################################################## - @server.list_tools() async def list_tools() -> list[MCPTool]: + """ + List all available tools + """ + return await _list_mcp_tools() + + async def _list_mcp_tools() -> List[MCPTool]: """ List all available tools """ @@ -67,24 +93,116 @@ if MCP_AVAILABLE: inputSchema=tool.input_schema, ) ) - + verbose_logger.debug( + "GLOBAL MCP TOOLS: %s", global_mcp_tool_registry.list_tools() + ) + sse_tools: List[MCPTool] = await global_mcp_server_manager.list_tools() + verbose_logger.debug("SSE TOOLS: %s", sse_tools) + if sse_tools is not None: + tools.extend(sse_tools) return tools @server.call_tool() - async def handle_call_tool( + async def mcp_server_tool_call( name: str, arguments: Dict[str, Any] | None ) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]: """ Call a specific tool with the provided arguments + + Args: + name (str): Name of the tool to call + arguments (Dict[str, Any] | None): Arguments to pass to the tool + + Returns: + List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]: Tool execution results + + Raises: + HTTPException: If tool not found or arguments missing + """ + # Validate arguments + response = await call_mcp_tool( + name=name, + arguments=arguments, + ) + return response + + @client + async def call_mcp_tool( + name: str, arguments: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]: + """ + Call a specific tool with the provided arguments """ - tool = global_mcp_tool_registry.get_tool(name) - if not tool: - raise HTTPException(status_code=404, detail=f"Tool '{name}' not found") if arguments is None: raise HTTPException( status_code=400, detail="Request arguments are required" ) + standard_logging_mcp_tool_call: StandardLoggingMCPToolCall = ( + _get_standard_logging_mcp_tool_call( + name=name, + arguments=arguments, + ) + ) + litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get( + "litellm_logging_obj", None + ) + if litellm_logging_obj: + litellm_logging_obj.model_call_details["mcp_tool_call_metadata"] = ( + standard_logging_mcp_tool_call + ) + litellm_logging_obj.model_call_details["model"] = ( + f"{MCP_TOOL_NAME_PREFIX}: {standard_logging_mcp_tool_call.get('name') or ''}" + ) + litellm_logging_obj.model_call_details["custom_llm_provider"] = ( + standard_logging_mcp_tool_call.get("mcp_server_name") + ) + + # Try managed server tool first + if name in global_mcp_server_manager.tool_name_to_mcp_server_name_mapping: + return await _handle_managed_mcp_tool(name, arguments) + + # Fall back to local tool registry + return await _handle_local_mcp_tool(name, arguments) + + def _get_standard_logging_mcp_tool_call( + name: str, + arguments: Dict[str, Any], + ) -> StandardLoggingMCPToolCall: + mcp_server = global_mcp_server_manager._get_mcp_server_from_tool_name(name) + if mcp_server: + mcp_info = mcp_server.mcp_info or {} + return StandardLoggingMCPToolCall( + name=name, + arguments=arguments, + mcp_server_name=mcp_info.get("server_name"), + mcp_server_logo_url=mcp_info.get("logo_url"), + ) + else: + return StandardLoggingMCPToolCall( + name=name, + arguments=arguments, + ) + + async def _handle_managed_mcp_tool( + name: str, arguments: Dict[str, Any] + ) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]: + """Handle tool execution for managed server tools""" + call_tool_result = await global_mcp_server_manager.call_tool( + name=name, + arguments=arguments, + ) + verbose_logger.debug("CALL TOOL RESULT: %s", call_tool_result) + return call_tool_result.content + + async def _handle_local_mcp_tool( + name: str, arguments: Dict[str, Any] + ) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]: + """Handle tool execution for local registry tools""" + tool = global_mcp_tool_registry.get_tool(name) + if not tool: + raise HTTPException(status_code=404, detail=f"Tool '{name}' not found") + try: result = tool.handler(**arguments) return [MCPTextContent(text=str(result), type="text")] @@ -113,6 +231,74 @@ if MCP_AVAILABLE: await sse.handle_post_message(request.scope, request.receive, request._send) await request.close() + ######################################################## + ############ MCP Server REST API Routes ################# + ######################################################## + @router.get("/tools/list", dependencies=[Depends(user_api_key_auth)]) + async def list_tool_rest_api() -> List[ListMCPToolsRestAPIResponseObject]: + """ + List all available tools with information about the server they belong to. + + Example response: + Tools: + [ + { + "name": "create_zap", + "description": "Create a new zap", + "inputSchema": "tool_input_schema", + "mcp_info": { + "server_name": "zapier", + "logo_url": "https://www.zapier.com/logo.png", + } + }, + { + "name": "fetch_data", + "description": "Fetch data from a URL", + "inputSchema": "tool_input_schema", + "mcp_info": { + "server_name": "fetch", + "logo_url": "https://www.fetch.com/logo.png", + } + } + ] + """ + list_tools_result: List[ListMCPToolsRestAPIResponseObject] = [] + for server in global_mcp_server_manager.mcp_servers: + try: + tools = await global_mcp_server_manager._get_tools_from_server(server) + for tool in tools: + list_tools_result.append( + ListMCPToolsRestAPIResponseObject( + name=tool.name, + description=tool.description, + inputSchema=tool.inputSchema, + mcp_info=server.mcp_info, + ) + ) + except Exception as e: + verbose_logger.exception(f"Error getting tools from {server.name}: {e}") + continue + return list_tools_result + + @router.post("/tools/call", dependencies=[Depends(user_api_key_auth)]) + async def call_tool_rest_api( + request: Request, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), + ): + """ + REST API to call a specific MCP tool with the provided arguments + """ + from litellm.proxy.proxy_server import add_litellm_data_to_request, proxy_config + + data = await request.json() + data = await add_litellm_data_to_request( + data=data, + request=request, + user_api_key_dict=user_api_key_dict, + proxy_config=proxy_config, + ) + return await call_mcp_tool(**data) + options = InitializationOptions( server_name="litellm-mcp-server", server_version="0.1.0", diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-601568e45a5ffece.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-601568e45a5ffece.js new file mode 100644 index 0000000000..e6ec4d178e --- /dev/null +++ b/litellm/proxy/_experimental/out/_next/static/chunks/250-601568e45a5ffece.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 M},AZ:function(){return U},Au:function(){return ep},BL:function(){return eI},Br:function(){return F},E9:function(){return eV},EG:function(){return eD},EY:function(){return eM},Eb:function(){return N},FC:function(){return ea},Gh:function(){return eb},H1:function(){return v},H2:function(){return n},Hx:function(){return eu},I1:function(){return j},It:function(){return x},J$:function(){return W},K8:function(){return d},K_:function(){return eH},LY:function(){return ev},Lp:function(){return eB},N3:function(){return eT},N8:function(){return $},NL:function(){return eQ},NV:function(){return f},Nc:function(){return eC},O3:function(){return eJ},OD:function(){return ef},OU:function(){return es},Of:function(){return b},Og:function(){return y},Ov:function(){return E},PT:function(){return D},Qg:function(){return eE},RQ:function(){return _},Rg:function(){return X},Sb:function(){return eP},So:function(){return Q},Tj:function(){return eq},VA:function(){return G},Vt:function(){return eU},W_:function(){return J},X:function(){return ee},XO:function(){return k},Xd:function(){return em},Xm:function(){return S},YU:function(){return eR},Zr:function(){return m},a6:function(){return B},ao:function(){return eZ},b1:function(){return ec},cq:function(){return A},cu:function(){return eS},eH:function(){return H},eZ:function(){return eN},fP:function(){return K},g:function(){return eX},gX:function(){return ej},h3:function(){return er},hT:function(){return ek},hy:function(){return u},ix:function(){return L},j2:function(){return et},jA:function(){return eL},jE:function(){return eA},kK:function(){return p},kn:function(){return Z},lP:function(){return h},lg:function(){return eg},mR:function(){return Y},m_:function(){return I},mp:function(){return ez},n$:function(){return eh},nd:function(){return eY},o6:function(){return q},oC:function(){return e_},pf:function(){return eG},qI:function(){return g},qk:function(){return e$},qm:function(){return w},r6:function(){return O},rs:function(){return C},s0:function(){return R},sN:function(){return ex},t$:function(){return P},t0:function(){return ey},t3:function(){return eK},tN:function(){return en},u5:function(){return eo},um:function(){return eF},v9:function(){return ew},vh:function(){return eO},wX:function(){return T},wd:function(){return el},xA:function(){return ed},zg:function(){return ei}});var r=o(20347),a=o(41021);let n=null;console.log=function(){};let c=0,s=e=>new Promise(t=>setTimeout(t,e)),l=async e=>{let t=Date.now();t-c>6e4?(e.includes("Authentication Error - Expired Key")&&(a.ZP.info("UI Session Expired. Logging out."),c=t,await s(3e3),document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href="/"),c=t):console.log("Error suppressed to prevent spam:",e)},i="Authorization";function d(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),i=e}let h=async()=>{let e=n?"".concat(n,"/openapi.json"):"/openapi.json",t=await fetch(e);return await t.json()},w=async e=>{try{let t=n?"".concat(n,"/get/litellm_model_cost_map"):"/get/litellm_model_cost_map",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}}),r=await o.json();return console.log("received litellm model cost data: ".concat(r)),r}catch(e){throw console.error("Failed to get model cost map:",e),e}},p=async(e,t)=>{try{let o=n?"".concat(n,"/model/new"):"/model/new",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text()||"Network response was not ok";throw a.ZP.error(e),Error(e)}let c=await r.json();return console.log("API Response:",c),a.ZP.destroy(),a.ZP.success("Model ".concat(t.model_name," created successfully"),2),c}catch(e){throw console.error("Failed to create key:",e),e}},u=async e=>{try{let t=n?"".concat(n,"/model/settings"):"/model/settings",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){console.error("Failed to get model settings:",e)}},y=async(e,t)=>{console.log("model_id in model delete call: ".concat(t));try{let o=n?"".concat(n,"/model/delete"):"/model/delete",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!r.ok){let e=await r.text();throw l(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}},f=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=n?"".concat(n,"/budget/delete"):"/budget/delete",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});if(!r.ok){let e=await r.text();throw l(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}},m=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=n?"".concat(n,"/budget/new"):"/budget/new",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(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}},g=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=n?"".concat(n,"/budget/update"):"/budget/update",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(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}},k=async(e,t)=>{try{let o=n?"".concat(n,"/invitation/new"):"/invitation/new",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});if(!r.ok){let e=await r.text();throw l(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}},_=async e=>{try{let t=n?"".concat(n,"/alerting/settings"):"/alerting/settings",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},T=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=n?"".concat(n,"/key/generate"):"/key/generate",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error(e)}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},E=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=n?"".concat(n,"/user/new"):"/user/new",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error(e)}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let o=n?"".concat(n,"/key/delete"):"/key/delete";console.log("in keyDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},N=async(e,t)=>{try{let o=n?"".concat(n,"/user/delete"):"/user/delete";console.log("in userDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to delete user(s):",e),e}},C=async(e,t)=>{try{let o=n?"".concat(n,"/team/delete"):"/team/delete";console.log("in teamDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to delete key:",e),e}},b=async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;try{let a=n?"".concat(n,"/user/list"):"/user/list";console.log("in userListCall");let c=new URLSearchParams;if(t&&t.length>0){let e=t.join(",");c.append("user_ids",e)}o&&c.append("page",o.toString()),r&&c.append("page_size",r.toString());let s=c.toString();s&&(a+="?".concat(s));let d=await fetch(a,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!d.ok){let e=await d.text();throw l(e),Error("Network response was not ok")}let h=await d.json();return console.log("/user/list API Response:",h),h}catch(e){throw console.error("Failed to create key:",e),e}},F=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4?arguments[4]:void 0,c=arguments.length>5?arguments[5]:void 0;try{let s;if(r){s=n?"".concat(n,"/user/list"):"/user/list";let e=new URLSearchParams;null!=a&&e.append("page",a.toString()),null!=c&&e.append("page_size",c.toString()),s+="?".concat(e.toString())}else s=n?"".concat(n,"/user/info"):"/user/info","Admin"===o||"Admin Viewer"===o||t&&(s+="?user_id=".concat(t));console.log("Requesting user data from:",s);let d=await fetch(s,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!d.ok){let e=await d.text();throw l(e),Error("Network response was not ok")}let h=await d.json();return console.log("API Response:",h),h}catch(e){throw console.error("Failed to fetch user data:",e),e}},S=async(e,t)=>{try{let o=n?"".concat(n,"/team/info"):"/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(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}},x=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;try{let r=n?"".concat(n,"/team/list"):"/team/list";console.log("in teamInfoCall");let a=new URLSearchParams;o&&a.append("user_id",o.toString()),t&&a.append("organization_id",t.toString());let c=a.toString();c&&(r+="?".concat(c));let s=await fetch(r,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}let d=await s.json();return console.log("/team/list API Response:",d),d}catch(e){throw console.error("Failed to create key:",e),e}},B=async e=>{try{let t=n?"".concat(n,"/team/available"):"/team/available";console.log("in availableTeamListCall");let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}let r=await o.json();return console.log("/team/available_teams API Response:",r),r}catch(e){throw e}},O=async e=>{try{let t=n?"".concat(n,"/organization/list"):"/organization/list",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},P=async(e,t)=>{try{let o=n?"".concat(n,"/organization/info"):"/organization/info";t&&(o="".concat(o,"?organization_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(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}},v=async(e,t)=>{try{if(console.log("Form Values in organizationCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw console.error("Failed to parse metadata:",e),Error("Failed to parse metadata: "+e)}}let o=n?"".concat(n,"/organization/new"):"/organization/new",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(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}},G=async(e,t)=>{try{console.log("Form Values in organizationUpdateCall:",t);let o=n?"".concat(n,"/organization/update"):"/organization/update",r=await fetch(o,{method:"PATCH",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("Update Team Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},A=async(e,t)=>{try{let o=n?"".concat(n,"/organization/delete"):"/organization/delete",r=await fetch(o,{method:"DELETE",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_ids:[t]})});if(!r.ok){let e=await r.text();throw l(e),Error("Error deleting organization: ".concat(e))}return await r.json()}catch(e){throw console.error("Failed to delete organization:",e),e}},J=async e=>{try{let t=n?"".concat(n,"/onboarding/get_token"):"/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 l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},I=async(e,t,o,r)=>{let a=n?"".concat(n,"/onboarding/claim_token"):"/onboarding/claim_token";try{let n=await fetch(a,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({invitation_link:t,user_id:o,password:r})});if(!n.ok){let e=await n.text();throw l(e),Error("Network response was not ok")}let c=await n.json();return console.log(c),c}catch(e){throw console.error("Failed to delete key:",e),e}},R=async(e,t,o)=>{try{let r=n?"".concat(n,"/key/").concat(t,"/regenerate"):"/key/".concat(t,"/regenerate"),a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let c=await a.json();return console.log("Regenerate key Response:",c),c}catch(e){throw console.error("Failed to regenerate key:",e),e}},z=!1,V=null,U=async(e,t,o)=>{try{console.log("modelInfoCall:",e,t,o);let c=n?"".concat(n,"/v2/model/info"):"/v2/model/info";r.ZL.includes(o)||(c+="?user_models_only=true");let s=await fetch(c,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw e+="error shown=".concat(z),z||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),a.ZP.info(e,10),z=!0,V&&clearTimeout(V),V=setTimeout(()=>{z=!1},1e4)),Error("Network response was not ok")}let l=await s.json();return console.log("modelInfoCall:",l),l}catch(e){throw console.error("Failed to create key:",e),e}},L=async(e,t)=>{try{let o=n?"".concat(n,"/v1/model/info"):"/v1/model/info";o+="?litellm_model_id=".concat(t);let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok)throw await r.text(),Error("Network response was not ok");let a=await r.json();return console.log("modelInfoV1Call:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},Z=async e=>{try{let t=n?"".concat(n,"/model_group/info"):"/model_group/info",o=await fetch(t,{method:"GET",headers:{[i]:"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("modelHubCall:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},D=async e=>{try{let t=n?"".concat(n,"/get/allowed_ips"):"/get/allowed_ips",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("getAllowedIPs:",r),r.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},H=async(e,t)=>{try{let o=n?"".concat(n,"/add/allowed_ip"):"/add/allowed_ip",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!r.ok){let e=await r.text();throw Error("Network response was not ok: ".concat(e))}let a=await r.json();return console.log("addAllowedIP:",a),a}catch(e){throw console.error("Failed to add allowed IP:",e),e}},M=async(e,t)=>{try{let o=n?"".concat(n,"/delete/allowed_ip"):"/delete/allowed_ip",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!r.ok){let e=await r.text();throw Error("Network response was not ok: ".concat(e))}let a=await r.json();return console.log("deleteAllowedIP:",a),a}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},q=async(e,t,o,r,a,c,s,d)=>{try{let t=n?"".concat(n,"/model/metrics"):"/model/metrics";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},X=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/model/streaming_metrics"):"/model/streaming_metrics";t&&(a="".concat(a,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(r));let c=await fetch(a,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok){let e=await c.text();throw l(e),Error("Network response was not ok")}return await c.json()}catch(e){throw console.error("Failed to create key:",e),e}},K=async(e,t,o,r,a,c,s,d)=>{try{let t=n?"".concat(n,"/model/metrics/slow_responses"):"/model/metrics/slow_responses";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},$=async(e,t,o,r,a,c,s,d)=>{try{let t=n?"".concat(n,"/model/metrics/exceptions"):"/model/metrics/exceptions";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(a,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to create key:",e),e}},Q=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;console.log("in /models calls, globalLitellmHeaderName",i);try{let t=n?"".concat(n,"/models"):"/models",o=new URLSearchParams;!0===r&&o.append("return_wildcard_routes","True"),a&&o.append("team_id",a.toString()),o.toString()&&(t+="?".concat(o.toString()));let c=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok){let e=await c.text();throw l(e),Error("Network response was not ok")}return await c.json()}catch(e){throw console.error("Failed to create key:",e),e}},Y=async e=>{try{let t=n?"".concat(n,"/global/spend/teams"):"/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(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}},W=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/global/spend/tags"):"/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 c=await fetch("".concat(a),{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},ee=async e=>{try{let t=n?"".concat(n,"/global/spend/all_tag_names"):"/global/spend/all_tag_names";console.log("in global/spend/all_tag_names call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[i]:"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}},et=async e=>{try{let t=n?"".concat(n,"/global/all_end_users"):"/global/all_end_users";console.log("in global/all_end_users call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[i]:"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}},eo=async(e,t)=>{try{let o=n?"".concat(n,"/user/filter/ui"):"/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:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to create key:",e),e}},er=async(e,t,o,r,a,c,s,d,h)=>{try{let w=n?"".concat(n,"/spend/logs/ui"):"/spend/logs/ui",p=new URLSearchParams;t&&p.append("api_key",t),o&&p.append("team_id",o),r&&p.append("request_id",r),a&&p.append("start_date",a),c&&p.append("end_date",c),s&&p.append("page",s.toString()),d&&p.append("page_size",d.toString()),h&&p.append("user_id",h);let u=p.toString();u&&(w+="?".concat(u));let y=await fetch(w,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!y.ok){let e=await y.text();throw l(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}},ea=async e=>{try{let t=n?"".concat(n,"/global/spend/logs"):"/global/spend/logs",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(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}},en=async e=>{try{let t=n?"".concat(n,"/global/spend/keys?limit=5"):"/global/spend/keys?limit=5",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(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}},ec=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/global/spend/end_users"):"/global/spend/end_users",c="";c=t?JSON.stringify({api_key:t,startTime:o,endTime:r}):JSON.stringify({startTime:o,endTime:r});let s={method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:c},d=await fetch(a,s);if(!d.ok){let e=await d.text();throw l(e),Error("Network response was not ok")}let h=await d.json();return console.log(h),h}catch(e){throw console.error("Failed to create key:",e),e}},es=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/global/spend/provider"):"/global/spend/provider";o&&r&&(a+="?start_date=".concat(o,"&end_date=").concat(r)),t&&(a+="&api_key=".concat(t));let c={method:"GET",headers:{[i]:"Bearer ".concat(e)}},s=await fetch(a,c);if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}let d=await s.json();return console.log(d),d}catch(e){throw console.error("Failed to fetch spend data:",e),e}},el=async(e,t,o)=>{try{let r=n?"".concat(n,"/global/activity"):"/global/activity";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[i]:"Bearer ".concat(e)}},c=await fetch(r,a);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ei=async(e,t,o)=>{try{let r=n?"".concat(n,"/global/activity/cache_hits"):"/global/activity/cache_hits";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[i]:"Bearer ".concat(e)}},c=await fetch(r,a);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ed=async(e,t,o)=>{try{let r=n?"".concat(n,"/global/activity/model"):"/global/activity/model";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let a={method:"GET",headers:{[i]:"Bearer ".concat(e)}},c=await fetch(r,a);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},eh=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/global/activity/exceptions"):"/global/activity/exceptions";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let c={method:"GET",headers:{[i]:"Bearer ".concat(e)}},s=await fetch(a,c);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}},ew=async(e,t,o,r)=>{try{let a=n?"".concat(n,"/global/activity/exceptions/deployment"):"/global/activity/exceptions/deployment";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(a+="&model_group=".concat(r));let c={method:"GET",headers:{[i]:"Bearer ".concat(e)}},s=await fetch(a,c);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}},ep=async e=>{try{let t=n?"".concat(n,"/global/spend/models?limit=5"):"/global/spend/models?limit=5",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(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}},eu=async(e,t,o)=>{try{console.log("Sending model connection test request:",JSON.stringify(t));let a=n?"".concat(n,"/health/test_connection"):"/health/test_connection",c=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",[i]:"Bearer ".concat(e)},body:JSON.stringify({litellm_params:t,mode:o})}),s=c.headers.get("content-type");if(!s||!s.includes("application/json")){let e=await c.text();throw console.error("Received non-JSON response:",e),Error("Received non-JSON response (".concat(c.status,": ").concat(c.statusText,"). Check network tab for details."))}let l=await c.json();if(!c.ok||"error"===l.status){if("error"===l.status);else{var r;return{status:"error",message:(null===(r=l.error)||void 0===r?void 0:r.message)||"Connection test failed: ".concat(c.status," ").concat(c.statusText)}}}return l}catch(e){throw console.error("Model connection test error:",e),e}},ey=async(e,t)=>{try{console.log("entering keyInfoV1Call");let o=n?"".concat(n,"/key/info"):"/key/info";o="".concat(o,"?key=").concat(t);let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(console.log("response",r),!r.ok){let e=await r.text();l(e),a.ZP.error("Failed to fetch key info - "+e)}let c=await r.json();return console.log("data",c),c}catch(e){throw console.error("Failed to fetch key info:",e),e}},ef=async(e,t,o,r,a)=>{try{let c=n?"".concat(n,"/key/list"):"/key/list";console.log("in keyListCall");let s=new URLSearchParams;o&&s.append("team_id",o.toString()),t&&s.append("organization_id",t.toString()),r&&s.append("page",r.toString()),a&&s.append("size",a.toString()),s.append("return_full_object","true"),s.append("include_team_keys","true");let d=s.toString();d&&(c+="?".concat(d));let h=await fetch(c,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!h.ok){let e=await h.text();throw l(e),Error("Network response was not ok")}let w=await h.json();return console.log("/team/list API Response:",w),w}catch(e){throw console.error("Failed to create key:",e),e}},em=async(e,t)=>{try{let o=n?"".concat(n,"/user/get_users?role=").concat(t):"/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(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}},eg=async e=>{try{let t=n?"".concat(n,"/user/available_roles"):"/user/available_roles",o=await fetch(t,{method:"GET",headers:{[i]:"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("response from user/available_role",r),r}catch(e){throw e}},ek=async(e,t)=>{try{if(console.log("Form Values in teamCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let o=n?"".concat(n,"/team/new"):"/team/new",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(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}},e_=async(e,t)=>{try{if(console.log("Form Values in credentialCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let o=n?"".concat(n,"/credentials"):"/credentials",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(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}},eT=async e=>{try{let t=n?"".concat(n,"/credentials"):"/credentials";console.log("in credentialListCall");let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}let r=await o.json();return console.log("/credentials API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},eE=async(e,t,o)=>{try{let r=n?"".concat(n,"/credentials"):"/credentials";t?r+="/by_name/".concat(t):o&&(r+="/by_model/".concat(o)),console.log("in credentialListCall");let a=await fetch(r,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let c=await a.json();return console.log("/credentials API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},ej=async(e,t)=>{try{let o=n?"".concat(n,"/credentials/").concat(t):"/credentials/".concat(t);console.log("in credentialDeleteCall:",t);let r=await fetch(o,{method:"DELETE",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let a=await r.json();return console.log(a),a}catch(e){throw console.error("Failed to delete key:",e),e}},eN=async(e,t,o)=>{try{if(console.log("Form Values in credentialUpdateCall:",o),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let r=n?"".concat(n,"/credentials/").concat(t):"/credentials/".concat(t),a=await fetch(r,{method:"PATCH",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eC=async(e,t)=>{try{if(console.log("Form Values in keyUpdateCall:",t),t.model_tpm_limit){console.log("formValues.model_tpm_limit:",t.model_tpm_limit);try{t.model_tpm_limit=JSON.parse(t.model_tpm_limit)}catch(e){throw Error("Failed to parse model_tpm_limit: "+e)}}if(t.model_rpm_limit){console.log("formValues.model_rpm_limit:",t.model_rpm_limit);try{t.model_rpm_limit=JSON.parse(t.model_rpm_limit)}catch(e){throw Error("Failed to parse model_rpm_limit: "+e)}}let o=n?"".concat(n,"/key/update"):"/key/update",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("Update key Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},eb=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=n?"".concat(n,"/team/update"):"/team/update",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("Update Team Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},eF=async(e,t)=>{try{console.log("Form Values in modelUpateCall:",t);let o=n?"".concat(n,"/model/update"):"/model/update",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let a=await r.json();return console.log("Update model Response:",a),a}catch(e){throw console.error("Failed to update model:",e),e}},eS=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=n?"".concat(n,"/team/member_add"):"/team/member_add",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},ex=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=n?"".concat(n,"/team/member_update"):"/team/member_update",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eB=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=n?"".concat(n,"/team/member_delete"):"/team/member_delete",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,...void 0!==o.user_email&&{user_email:o.user_email},...void 0!==o.user_id&&{user_id:o.user_id}})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eO=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=n?"".concat(n,"/organization/member_add"):"/organization/member_add",a=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error(e)}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create organization member:",e),e}},eP=async(e,t,o)=>{try{console.log("Form Values in organizationMemberDeleteCall:",o);let r=n?"".concat(n,"/organization/member_delete"):"/organization/member_delete",a=await fetch(r,{method:"DELETE",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,user_id:o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to delete organization member:",e),e}},ev=async(e,t,o)=>{try{console.log("Form Values in organizationMemberUpdateCall:",o);let r=n?"".concat(n,"/organization/member_update"):"/organization/member_update",a=await fetch(r,{method:"PATCH",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,...o})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await a.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to update organization member:",e),e}},eG=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let r=n?"".concat(n,"/user/update"):"/user/update",a={...t};null!==o&&(a.user_role=o),a=JSON.stringify(a);let c=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:a});if(!c.ok){let e=await c.text();throw l(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let s=await c.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},eA=async(e,t)=>{try{let o=n?"".concat(n,"/health/services?service=").concat(t):"/health/services?service=".concat(t);console.log("Checking Slack Budget Alerts service health");let r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(e),Error(e)}let c=await r.json();return a.ZP.success("Test request to ".concat(t," made - check logs/alerts on ").concat(t," to verify")),c}catch(e){throw console.error("Failed to perform health check:",e),e}},eJ=async e=>{try{let t=n?"".concat(n,"/budget/list"):"/budget/list",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eI=async(e,t,o)=>{try{let t=n?"".concat(n,"/get/config/callbacks"):"/get/config/callbacks",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eR=async e=>{try{let t=n?"".concat(n,"/config/list?config_type=general_settings"):"/config/list?config_type=general_settings",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},ez=async e=>{try{let t=n?"".concat(n,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eV=async(e,t)=>{try{let o=n?"".concat(n,"/config/field/info?field_name=").concat(t):"/config/field/info?field_name=".concat(t),r=await fetch(o,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok)throw await r.text(),Error("Network response was not ok");return await r.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eU=async(e,t)=>{try{let o=n?"".concat(n,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eL=async(e,t,o)=>{try{let r=n?"".concat(n,"/config/field/update"):"/config/field/update",c=await fetch(r,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,field_value:o,config_type:"general_settings"})});if(!c.ok){let e=await c.text();throw l(e),Error("Network response was not ok")}let s=await c.json();return a.ZP.success("Successfully updated value!"),s}catch(e){throw console.error("Failed to set callbacks:",e),e}},eZ=async(e,t)=>{try{let o=n?"".concat(n,"/config/field/delete"):"/config/field/delete",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,config_type:"general_settings"})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let c=await r.json();return a.ZP.success("Field reset on proxy"),c}catch(e){throw console.error("Failed to get callbacks:",e),e}},eD=async(e,t)=>{try{let o=n?"".concat(n,"/config/pass_through_endpoint?endpoint_id=").concat(t):"/config/pass_through_endpoint".concat(t),r=await fetch(o,{method:"DELETE",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eH=async(e,t)=>{try{let o=n?"".concat(n,"/config/update"):"/config/update",r=await fetch(o,{method:"POST",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}return await r.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eM=async e=>{try{let t=n?"".concat(n,"/health"):"/health",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}return await o.json()}catch(e){throw console.error("Failed to call /health:",e),e}},eq=async e=>{try{let t=n?"".concat(n,"/cache/ping"):"/cache/ping",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error(e)}return await o.json()}catch(e){throw console.error("Failed to call /cache/ping:",e),e}},eX=async e=>{try{let t=n?"".concat(n,"/sso/get/ui_settings"):"/sso/get/ui_settings",o=await fetch(t,{method:"GET",headers:{[i]:"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 get callbacks:",e),e}},eK=async e=>{try{let t=n?"".concat(n,"/guardrails/list"):"/guardrails/list",o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}let r=await o.json();return console.log("Guardrails list response:",r),r}catch(e){throw console.error("Failed to fetch guardrails list:",e),e}},e$=async(e,t,o)=>{try{let r=n?"".concat(n,"/spend/logs/ui/").concat(t,"?start_date=").concat(encodeURIComponent(o)):"/spend/logs/ui/".concat(t,"?start_date=").concat(encodeURIComponent(o));console.log("Fetching log details from:",r);let a=await fetch(r,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let c=await a.json();return console.log("Fetched log details:",c),c}catch(e){throw console.error("Failed to fetch log details:",e),e}},eQ=async e=>{try{let t=n?"".concat(n,"/get/internal_user_settings"):"/get/internal_user_settings";console.log("Fetching SSO settings from:",t);let o=await fetch(t,{method:"GET",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Network response was not ok")}let r=await o.json();return console.log("Fetched SSO settings:",r),r}catch(e){throw console.error("Failed to fetch SSO settings:",e),e}},eY=async(e,t)=>{try{let o=n?"".concat(n,"/update/internal_user_settings"):"/update/internal_user_settings";console.log("Updating internal user settings:",t);let r=await fetch(o,{method:"PATCH",headers:{[i]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let e=await r.text();throw l(e),Error("Network response was not ok")}let c=await r.json();return console.log("Updated internal user settings:",c),a.ZP.success("Internal user settings updated successfully"),c}catch(e){throw console.error("Failed to update internal user settings:",e),e}}},20347:function(e,t,o){o.d(t,{LQ:function(){return n},ZL:function(){return r},lo:function(){return a}});let r=["Admin","Admin Viewer","proxy_admin","proxy_admin_viewer","org_admin"],a=["Internal User","Internal Viewer"],n=["Internal User","Admin"]}}]); \ No newline at end of file diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-a75ee9d79f1140b0.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-a75ee9d79f1140b0.js deleted file mode 100644 index 882840d12b..0000000000 --- a/litellm/proxy/_experimental/out/_next/static/chunks/250-a75ee9d79f1140b0.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 H},AZ:function(){return V},Au:function(){return ew},BL:function(){return eA},Br:function(){return b},E9:function(){return ez},EG:function(){return eZ},EY:function(){return eH},Eb:function(){return j},FC:function(){return er},Gh:function(){return eC},H1:function(){return P},H2:function(){return a},Hx:function(){return ep},I1:function(){return E},It:function(){return S},J$:function(){return Q},K8:function(){return i},K_:function(){return eD},LY:function(){return eP},Lp:function(){return ex},N3:function(){return e_},N8:function(){return K},NL:function(){return e$},NV:function(){return y},Nc:function(){return eN},O3:function(){return eJ},OD:function(){return ey},OU:function(){return ec},Of:function(){return C},Og:function(){return u},Ov:function(){return T},PT:function(){return Z},Qg:function(){return eT},RQ:function(){return k},Rg:function(){return q},Sb:function(){return eO},So:function(){return $},Tj:function(){return eM},VA:function(){return v},Vt:function(){return eV},W_:function(){return J},X:function(){return W},XO:function(){return g},Xd:function(){return ef},Xm:function(){return F},YU:function(){return eR},Zr:function(){return f},a6:function(){return x},ao:function(){return eL},b1:function(){return en},cq:function(){return G},cu:function(){return eF},eH:function(){return D},eZ:function(){return ej},fP:function(){return X},g:function(){return eq},gX:function(){return eE},h3:function(){return eo},hT:function(){return eg},hy:function(){return p},ix:function(){return U},j2:function(){return ee},jA:function(){return eU},jE:function(){return eG},kK:function(){return w},kn:function(){return L},lP:function(){return d},lg:function(){return em},mR:function(){return Y},m_:function(){return A},mp:function(){return eI},n$:function(){return ed},nd:function(){return eY},o6:function(){return M},oC:function(){return ek},pf:function(){return ev},qI:function(){return m},qk:function(){return eK},qm:function(){return h},r6:function(){return B},rs:function(){return N},s0:function(){return R},sN:function(){return eS},t$:function(){return O},t0:function(){return eu},t3:function(){return eX},tN:function(){return ea},u5:function(){return et},um:function(){return eb},v9:function(){return eh},vh:function(){return eB},wX:function(){return _},wd:function(){return es},xA:function(){return ei},zg:function(){return el}});var r=o(41021);let a=null;console.log=function(){};let n=0,c=e=>new Promise(t=>setTimeout(t,e)),s=async e=>{let t=Date.now();t-n>6e4?(e.includes("Authentication Error - Expired Key")&&(r.ZP.info("UI Session Expired. Logging out."),n=t,await c(3e3),document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href="/"),n=t):console.log("Error suppressed to prevent spam:",e)},l="Authorization";function i(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),l=e}let d=async()=>{let e=a?"".concat(a,"/openapi.json"):"/openapi.json",t=await fetch(e);return await t.json()},h=async e=>{try{let t=a?"".concat(a,"/get/litellm_model_cost_map"):"/get/litellm_model_cost_map",o=await fetch(t,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}}),r=await o.json();return console.log("received litellm model cost data: ".concat(r)),r}catch(e){throw console.error("Failed to get model cost map:",e),e}},w=async(e,t)=>{try{let c=a?"".concat(a,"/model/new"):"/model/new",s=await fetch(c,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!s.ok){var o,n;let e=await s.json(),t=(null===(n=e.error)||void 0===n?void 0:null===(o=n.message)||void 0===o?void 0:o.error)||"Network response was not ok";throw r.ZP.error(t),Error(t)}let i=await s.json();return console.log("API Response:",i),r.ZP.destroy(),r.ZP.success("Model ".concat(t.model_name," created successfully"),2),i}catch(e){throw console.error("Failed to create key:",e),e}},p=async e=>{try{let t=a?"".concat(a,"/model/settings"):"/model/settings",o=await fetch(t,{method:"GET",headers:{[l]:"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}},u=async(e,t)=>{console.log("model_id in model delete call: ".concat(t));try{let o=a?"".concat(a,"/model/delete"):"/model/delete",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},y=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=a?"".concat(a,"/budget/delete"):"/budget/delete",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},f=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=a?"".concat(a,"/budget/new"):"/budget/new",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=a?"".concat(a,"/budget/update"):"/budget/update",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},g=async(e,t)=>{try{let o=a?"".concat(a,"/invitation/new"):"/invitation/new",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},k=async e=>{try{let t=a?"".concat(a,"/alerting/settings"):"/alerting/settings",o=await fetch(t,{method:"GET",headers:{[l]:"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}},_=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=a?"".concat(a,"/key/generate"):"/key/generate",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},T=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=a?"".concat(a,"/user/new"):"/user/new",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},E=async(e,t)=>{try{let o=a?"".concat(a,"/key/delete"):"/key/delete";console.log("in keyDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},j=async(e,t)=>{try{let o=a?"".concat(a,"/user/delete"):"/user/delete";console.log("in userDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to delete user(s):",e),e}},N=async(e,t)=>{try{let o=a?"".concat(a,"/team/delete"):"/team/delete";console.log("in teamDeleteCall:",t);let r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!r.ok){let e=await r.text();throw s(e),Error("Network response was not ok")}let n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},C=async function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;try{let n=a?"".concat(a,"/user/list"):"/user/list";console.log("in userListCall");let c=new URLSearchParams;if(t&&t.length>0){let e=t.join(",");c.append("user_ids",e)}o&&c.append("page",o.toString()),r&&c.append("page_size",r.toString());let i=c.toString();i&&(n+="?".concat(i));let d=await fetch(n,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!d.ok){let e=await d.text();throw s(e),Error("Network response was not ok")}let h=await d.json();return console.log("/user/list API Response:",h),h}catch(e){throw console.error("Failed to create key:",e),e}},b=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],n=arguments.length>4?arguments[4]:void 0,c=arguments.length>5?arguments[5]:void 0;try{let i;if(r){i=a?"".concat(a,"/user/list"):"/user/list";let e=new URLSearchParams;null!=n&&e.append("page",n.toString()),null!=c&&e.append("page_size",c.toString()),i+="?".concat(e.toString())}else i=a?"".concat(a,"/user/info"):"/user/info","Admin"===o||"Admin Viewer"===o||t&&(i+="?user_id=".concat(t));console.log("Requesting user data from:",i);let d=await fetch(i,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!d.ok){let e=await d.text();throw s(e),Error("Network response was not ok")}let h=await d.json();return console.log("API Response:",h),h}catch(e){throw console.error("Failed to fetch user data:",e),e}},F=async(e,t)=>{try{let o=a?"".concat(a,"/team/info"):"/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[l]:"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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},S=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;try{let r=a?"".concat(a,"/team/list"):"/team/list";console.log("in teamInfoCall");let n=new URLSearchParams;o&&n.append("user_id",o.toString()),t&&n.append("organization_id",t.toString());let c=n.toString();c&&(r+="?".concat(c));let i=await fetch(r,{method:"GET",headers:{[l]:"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("/team/list API Response:",d),d}catch(e){throw console.error("Failed to create key:",e),e}},x=async e=>{try{let t=a?"".concat(a,"/team/available"):"/team/available";console.log("in availableTeamListCall");let o=await fetch(t,{method:"GET",headers:{[l]:"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("/team/available_teams API Response:",r),r}catch(e){throw e}},B=async e=>{try{let t=a?"".concat(a,"/organization/list"):"/organization/list",o=await fetch(t,{method:"GET",headers:{[l]:"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}},O=async(e,t)=>{try{let o=a?"".concat(a,"/organization/info"):"/organization/info";t&&(o="".concat(o,"?organization_id=").concat(t)),console.log("in teamInfoCall");let r=await fetch(o,{method:"GET",headers:{[l]:"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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},P=async(e,t)=>{try{if(console.log("Form Values in organizationCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw console.error("Failed to parse metadata:",e),Error("Failed to parse metadata: "+e)}}let o=a?"".concat(a,"/organization/new"):"/organization/new",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,t)=>{try{console.log("Form Values in organizationUpdateCall:",t);let o=a?"".concat(a,"/organization/update"):"/organization/update",r=await fetch(o,{method:"PATCH",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("Update Team Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},G=async(e,t)=>{try{let o=a?"".concat(a,"/organization/delete"):"/organization/delete",r=await fetch(o,{method:"DELETE",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_ids:[t]})});if(!r.ok){let e=await r.text();throw s(e),Error("Error deleting organization: ".concat(e))}return await r.json()}catch(e){throw console.error("Failed to delete organization:",e),e}},J=async e=>{try{let t=a?"".concat(a,"/onboarding/get_token"):"/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}},A=async(e,t,o,r)=>{let n=a?"".concat(a,"/onboarding/claim_token"):"/onboarding/claim_token";try{let a=await fetch(n,{method:"POST",headers:{[l]:"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 c=await a.json();return console.log(c),c}catch(e){throw console.error("Failed to delete key:",e),e}},R=async(e,t,o)=>{try{let r=a?"".concat(a,"/key/").concat(t,"/regenerate"):"/key/".concat(t,"/regenerate"),n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}let c=await n.json();return console.log("Regenerate key Response:",c),c}catch(e){throw console.error("Failed to regenerate key:",e),e}},I=!1,z=null,V=async(e,t,o)=>{try{let t=a?"".concat(a,"/v2/model/info"):"/v2/model/info",o=await fetch(t,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw e+="error shown=".concat(I),I||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),r.ZP.info(e,10),I=!0,z&&clearTimeout(z),z=setTimeout(()=>{I=!1},1e4)),Error("Network response was not ok")}let n=await o.json();return console.log("modelInfoCall:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,t)=>{try{let o=a?"".concat(a,"/v1/model/info"):"/v1/model/info";o+="?litellm_model_id=".concat(t);let r=await fetch(o,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok)throw await r.text(),Error("Network response was not ok");let n=await r.json();return console.log("modelInfoV1Call:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},L=async e=>{try{let t=a?"".concat(a,"/model_group/info"):"/model_group/info",o=await fetch(t,{method:"GET",headers:{[l]:"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("modelHubCall:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},Z=async e=>{try{let t=a?"".concat(a,"/get/allowed_ips"):"/get/allowed_ips",o=await fetch(t,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("getAllowedIPs:",r),r.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},D=async(e,t)=>{try{let o=a?"".concat(a,"/add/allowed_ip"):"/add/allowed_ip",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!r.ok){let e=await r.text();throw Error("Network response was not ok: ".concat(e))}let n=await r.json();return console.log("addAllowedIP:",n),n}catch(e){throw console.error("Failed to add allowed IP:",e),e}},H=async(e,t)=>{try{let o=a?"".concat(a,"/delete/allowed_ip"):"/delete/allowed_ip",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!r.ok){let e=await r.text();throw Error("Network response was not ok: ".concat(e))}let n=await r.json();return console.log("deleteAllowedIP:",n),n}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},M=async(e,t,o,r,n,c,i,d)=>{try{let t=a?"".concat(a,"/model/metrics"):"/model/metrics";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(i,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[l]:"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}},q=async(e,t,o,r)=>{try{let n=a?"".concat(a,"/model/streaming_metrics"):"/model/streaming_metrics";t&&(n="".concat(n,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(r));let c=await fetch(n,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok){let e=await c.text();throw s(e),Error("Network response was not ok")}return await c.json()}catch(e){throw console.error("Failed to create key:",e),e}},X=async(e,t,o,r,n,c,i,d)=>{try{let t=a?"".concat(a,"/model/metrics/slow_responses"):"/model/metrics/slow_responses";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(i,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[l]:"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}},K=async(e,t,o,r,n,c,i,d)=>{try{let t=a?"".concat(a,"/model/metrics/exceptions"):"/model/metrics/exceptions";r&&(t="".concat(t,"?_selected_model_group=").concat(r,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(i,"&customer=").concat(d));let o=await fetch(t,{method:"GET",headers:{[l]:"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}},$=async function(e,t,o){let r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;console.log("in /models calls, globalLitellmHeaderName",l);try{let t=a?"".concat(a,"/models"):"/models",o=new URLSearchParams;!0===r&&o.append("return_wildcard_routes","True"),n&&o.append("team_id",n.toString()),o.toString()&&(t+="?".concat(o.toString()));let c=await fetch(t,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok){let e=await c.text();throw s(e),Error("Network response was not ok")}return await c.json()}catch(e){throw console.error("Failed to create key:",e),e}},Y=async e=>{try{let t=a?"".concat(a,"/global/spend/teams"):"/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[l]:"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 n=a?"".concat(a,"/global/spend/tags"):"/global/spend/tags";t&&o&&(n="".concat(n,"?start_date=").concat(t,"&end_date=").concat(o)),r&&(n+="".concat(n,"&tags=").concat(r.join(","))),console.log("in tagsSpendLogsCall:",n);let c=await fetch("".concat(n),{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to create key:",e),e}},W=async e=>{try{let t=a?"".concat(a,"/global/spend/all_tag_names"):"/global/spend/all_tag_names";console.log("in global/spend/all_tag_names call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[l]:"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}},ee=async e=>{try{let t=a?"".concat(a,"/global/all_end_users"):"/global/all_end_users";console.log("in global/all_end_users call",t);let o=await fetch("".concat(t),{method:"GET",headers:{[l]:"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}},et=async(e,t)=>{try{let o=a?"".concat(a,"/user/filter/ui"):"/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:{[l]:"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}},eo=async(e,t,o,r,n,c,i,d,h)=>{try{let w=a?"".concat(a,"/spend/logs/ui"):"/spend/logs/ui",p=new URLSearchParams;t&&p.append("api_key",t),o&&p.append("team_id",o),r&&p.append("request_id",r),n&&p.append("start_date",n),c&&p.append("end_date",c),i&&p.append("page",i.toString()),d&&p.append("page_size",d.toString()),h&&p.append("user_id",h);let u=p.toString();u&&(w+="?".concat(u));let y=await fetch(w,{method:"GET",headers:{[l]:"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}},er=async e=>{try{let t=a?"".concat(a,"/global/spend/logs"):"/global/spend/logs",o=await fetch(t,{method:"GET",headers:{[l]:"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}},ea=async e=>{try{let t=a?"".concat(a,"/global/spend/keys?limit=5"):"/global/spend/keys?limit=5",o=await fetch(t,{method:"GET",headers:{[l]:"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}},en=async(e,t,o,r)=>{try{let n=a?"".concat(a,"/global/spend/end_users"):"/global/spend/end_users",c="";c=t?JSON.stringify({api_key:t,startTime:o,endTime:r}):JSON.stringify({startTime:o,endTime:r});let i={method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:c},d=await fetch(n,i);if(!d.ok){let e=await d.text();throw s(e),Error("Network response was not ok")}let h=await d.json();return console.log(h),h}catch(e){throw console.error("Failed to create key:",e),e}},ec=async(e,t,o,r)=>{try{let n=a?"".concat(a,"/global/spend/provider"):"/global/spend/provider";o&&r&&(n+="?start_date=".concat(o,"&end_date=").concat(r)),t&&(n+="&api_key=".concat(t));let c={method:"GET",headers:{[l]:"Bearer ".concat(e)}},i=await fetch(n,c);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(d),d}catch(e){throw console.error("Failed to fetch spend data:",e),e}},es=async(e,t,o)=>{try{let r=a?"".concat(a,"/global/activity"):"/global/activity";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[l]:"Bearer ".concat(e)}},c=await fetch(r,n);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},el=async(e,t,o)=>{try{let r=a?"".concat(a,"/global/activity/cache_hits"):"/global/activity/cache_hits";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[l]:"Bearer ".concat(e)}},c=await fetch(r,n);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ei=async(e,t,o)=>{try{let r=a?"".concat(a,"/global/activity/model"):"/global/activity/model";t&&o&&(r+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[l]:"Bearer ".concat(e)}},c=await fetch(r,n);if(!c.ok)throw await c.text(),Error("Network response was not ok");let s=await c.json();return console.log(s),s}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ed=async(e,t,o,r)=>{try{let n=a?"".concat(a,"/global/activity/exceptions"):"/global/activity/exceptions";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(n+="&model_group=".concat(r));let c={method:"GET",headers:{[l]:"Bearer ".concat(e)}},s=await fetch(n,c);if(!s.ok)throw await s.text(),Error("Network response was not ok");let i=await s.json();return console.log(i),i}catch(e){throw console.error("Failed to fetch spend data:",e),e}},eh=async(e,t,o,r)=>{try{let n=a?"".concat(a,"/global/activity/exceptions/deployment"):"/global/activity/exceptions/deployment";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),r&&(n+="&model_group=".concat(r));let c={method:"GET",headers:{[l]:"Bearer ".concat(e)}},s=await fetch(n,c);if(!s.ok)throw await s.text(),Error("Network response was not ok");let i=await s.json();return console.log(i),i}catch(e){throw console.error("Failed to fetch spend data:",e),e}},ew=async e=>{try{let t=a?"".concat(a,"/global/spend/models?limit=5"):"/global/spend/models?limit=5",o=await fetch(t,{method:"GET",headers:{[l]:"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}},ep=async(e,t,o)=>{try{console.log("Sending model connection test request:",JSON.stringify(t));let n=a?"".concat(a,"/health/test_connection"):"/health/test_connection",c=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",[l]:"Bearer ".concat(e)},body:JSON.stringify({litellm_params:t,mode:o})}),s=c.headers.get("content-type");if(!s||!s.includes("application/json")){let e=await c.text();throw console.error("Received non-JSON response:",e),Error("Received non-JSON response (".concat(c.status,": ").concat(c.statusText,"). Check network tab for details."))}let i=await c.json();if(!c.ok||"error"===i.status){if("error"===i.status);else{var r;return{status:"error",message:(null===(r=i.error)||void 0===r?void 0:r.message)||"Connection test failed: ".concat(c.status," ").concat(c.statusText)}}}return i}catch(e){throw console.error("Model connection test error:",e),e}},eu=async(e,t)=>{try{let o=a?"".concat(a,"/key/info"):"/key/info";o="".concat(o,"?key=").concat(t);let r=await fetch(o,{method:"GET",headers:{[l]:"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 fetch key info:",e),e}},ey=async(e,t,o,r,n)=>{try{let c=a?"".concat(a,"/key/list"):"/key/list";console.log("in keyListCall");let i=new URLSearchParams;o&&i.append("team_id",o.toString()),t&&i.append("organization_id",t.toString()),r&&i.append("page",r.toString()),n&&i.append("size",n.toString()),i.append("return_full_object","true"),i.append("include_team_keys","true");let d=i.toString();d&&(c+="?".concat(d));let h=await fetch(c,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!h.ok){let e=await h.text();throw s(e),Error("Network response was not ok")}let w=await h.json();return console.log("/team/list API Response:",w),w}catch(e){throw console.error("Failed to create key:",e),e}},ef=async(e,t)=>{try{let o=a?"".concat(a,"/user/get_users?role=").concat(t):"/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let r=await fetch(o,{method:"GET",headers:{[l]:"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 n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to get requested models:",e),e}},em=async e=>{try{let t=a?"".concat(a,"/user/available_roles"):"/user/available_roles",o=await fetch(t,{method:"GET",headers:{[l]:"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("response from user/available_role",r),r}catch(e){throw e}},eg=async(e,t)=>{try{if(console.log("Form Values in teamCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let o=a?"".concat(a,"/team/new"):"/team/new",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},ek=async(e,t)=>{try{if(console.log("Form Values in credentialCreateCall:",t),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let o=a?"".concat(a,"/credentials"):"/credentials",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},e_=async e=>{try{let t=a?"".concat(a,"/credentials"):"/credentials";console.log("in credentialListCall");let o=await fetch(t,{method:"GET",headers:{[l]:"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("/credentials API Response:",r),r}catch(e){throw console.error("Failed to create key:",e),e}},eT=async(e,t,o)=>{try{let r=a?"".concat(a,"/credentials"):"/credentials";t?r+="/by_name/".concat(t):o&&(r+="/by_model/".concat(o)),console.log("in credentialListCall");let n=await fetch(r,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}let c=await n.json();return console.log("/credentials API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eE=async(e,t)=>{try{let o=a?"".concat(a,"/credentials/").concat(t):"/credentials/".concat(t);console.log("in credentialDeleteCall:",t);let r=await fetch(o,{method:"DELETE",headers:{[l]:"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 n=await r.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},ej=async(e,t,o)=>{try{if(console.log("Form Values in credentialUpdateCall:",o),o.metadata){console.log("formValues.metadata:",o.metadata);try{o.metadata=JSON.parse(o.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}let r=a?"".concat(a,"/credentials/").concat(t):"/credentials/".concat(t),n=await fetch(r,{method:"PATCH",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eN=async(e,t)=>{try{if(console.log("Form Values in keyUpdateCall:",t),t.model_tpm_limit){console.log("formValues.model_tpm_limit:",t.model_tpm_limit);try{t.model_tpm_limit=JSON.parse(t.model_tpm_limit)}catch(e){throw Error("Failed to parse model_tpm_limit: "+e)}}if(t.model_rpm_limit){console.log("formValues.model_rpm_limit:",t.model_rpm_limit);try{t.model_rpm_limit=JSON.parse(t.model_rpm_limit)}catch(e){throw Error("Failed to parse model_rpm_limit: "+e)}}let o=a?"".concat(a,"/key/update"):"/key/update",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("Update key Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eC=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=a?"".concat(a,"/team/update"):"/team/update",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await r.json();return console.log("Update Team Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eb=async(e,t)=>{try{console.log("Form Values in modelUpateCall:",t);let o=a?"".concat(a,"/model/update"):"/model/update",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!r.ok){let e=await r.text();throw s(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let n=await r.json();return console.log("Update model Response:",n),n}catch(e){throw console.error("Failed to update model:",e),e}},eF=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=a?"".concat(a,"/team/member_add"):"/team/member_add",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eS=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=a?"".concat(a,"/team/member_update"):"/team/member_update",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},ex=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=a?"".concat(a,"/team/member_delete"):"/team/member_delete",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,...void 0!==o.user_email&&{user_email:o.user_email},...void 0!==o.user_id&&{user_id:o.user_id}})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eB=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let r=a?"".concat(a,"/organization/member_add"):"/organization/member_add",n=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error(e)}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to create organization member:",e),e}},eO=async(e,t,o)=>{try{console.log("Form Values in organizationMemberDeleteCall:",o);let r=a?"".concat(a,"/organization/member_delete"):"/organization/member_delete",n=await fetch(r,{method:"DELETE",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,user_id:o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to delete organization member:",e),e}},eP=async(e,t,o)=>{try{console.log("Form Values in organizationMemberUpdateCall:",o);let r=a?"".concat(a,"/organization/member_update"):"/organization/member_update",n=await fetch(r,{method:"PATCH",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,...o})});if(!n.ok){let e=await n.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("API Response:",c),c}catch(e){throw console.error("Failed to update organization member:",e),e}},ev=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let r=a?"".concat(a,"/user/update"):"/user/update",n={...t};null!==o&&(n.user_role=o),n=JSON.stringify(n);let c=await fetch(r,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:n});if(!c.ok){let e=await c.text();throw s(e),console.error("Error response from the server:",e),Error("Network response was not ok")}let i=await c.json();return console.log("API Response:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},eG=async(e,t)=>{try{let o=a?"".concat(a,"/health/services?service=").concat(t):"/health/services?service=".concat(t);console.log("Checking Slack Budget Alerts service health");let n=await fetch(o,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw s(e),Error(e)}let c=await n.json();return r.ZP.success("Test request to ".concat(t," made - check logs/alerts on ").concat(t," to verify")),c}catch(e){throw console.error("Failed to perform health check:",e),e}},eJ=async e=>{try{let t=a?"".concat(a,"/budget/list"):"/budget/list",o=await fetch(t,{method:"GET",headers:{[l]:"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}},eA=async(e,t,o)=>{try{let t=a?"".concat(a,"/get/config/callbacks"):"/get/config/callbacks",o=await fetch(t,{method:"GET",headers:{[l]:"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}},eR=async e=>{try{let t=a?"".concat(a,"/config/list?config_type=general_settings"):"/config/list?config_type=general_settings",o=await fetch(t,{method:"GET",headers:{[l]:"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}},eI=async e=>{try{let t=a?"".concat(a,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",o=await fetch(t,{method:"GET",headers:{[l]:"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}},ez=async(e,t)=>{try{let o=a?"".concat(a,"/config/field/info?field_name=").concat(t):"/config/field/info?field_name=".concat(t),r=await fetch(o,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!r.ok)throw await r.text(),Error("Network response was not ok");return await r.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eV=async(e,t)=>{try{let o=a?"".concat(a,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 set callbacks:",e),e}},eU=async(e,t,o)=>{try{let n=a?"".concat(a,"/config/field/update"):"/config/field/update",c=await fetch(n,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,field_value:o,config_type:"general_settings"})});if(!c.ok){let e=await c.text();throw s(e),Error("Network response was not ok")}let i=await c.json();return r.ZP.success("Successfully updated value!"),i}catch(e){throw console.error("Failed to set callbacks:",e),e}},eL=async(e,t)=>{try{let o=a?"".concat(a,"/config/field/delete"):"/config/field/delete",n=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({field_name:t,config_type:"general_settings"})});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}let c=await n.json();return r.ZP.success("Field reset on proxy"),c}catch(e){throw console.error("Failed to get callbacks:",e),e}},eZ=async(e,t)=>{try{let o=a?"".concat(a,"/config/pass_through_endpoint?endpoint_id=").concat(t):"/config/pass_through_endpoint".concat(t),r=await fetch(o,{method:"DELETE",headers:{[l]:"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 get callbacks:",e),e}},eD=async(e,t)=>{try{let o=a?"".concat(a,"/config/update"):"/config/update",r=await fetch(o,{method:"POST",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 set callbacks:",e),e}},eH=async e=>{try{let t=a?"".concat(a,"/health"):"/health",o=await fetch(t,{method:"GET",headers:{[l]:"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 call /health:",e),e}},eM=async e=>{try{let t=a?"".concat(a,"/cache/ping"):"/cache/ping",o=await fetch(t,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw s(e),Error(e)}return await o.json()}catch(e){throw console.error("Failed to call /cache/ping:",e),e}},eq=async e=>{try{let t=a?"".concat(a,"/sso/get/ui_settings"):"/sso/get/ui_settings",o=await fetch(t,{method:"GET",headers:{[l]:"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 get callbacks:",e),e}},eX=async e=>{try{let t=a?"".concat(a,"/guardrails/list"):"/guardrails/list",o=await fetch(t,{method:"GET",headers:{[l]:"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("Guardrails list response:",r),r}catch(e){throw console.error("Failed to fetch guardrails list:",e),e}},eK=async(e,t,o)=>{try{let r=a?"".concat(a,"/spend/logs/ui/").concat(t,"?start_date=").concat(encodeURIComponent(o)):"/spend/logs/ui/".concat(t,"?start_date=").concat(encodeURIComponent(o));console.log("Fetching log details from:",r);let n=await fetch(r,{method:"GET",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}let c=await n.json();return console.log("Fetched log details:",c),c}catch(e){throw console.error("Failed to fetch log details:",e),e}},e$=async e=>{try{let t=a?"".concat(a,"/get/internal_user_settings"):"/get/internal_user_settings";console.log("Fetching SSO settings from:",t);let o=await fetch(t,{method:"GET",headers:{[l]:"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("Fetched SSO settings:",r),r}catch(e){throw console.error("Failed to fetch SSO settings:",e),e}},eY=async(e,t)=>{try{let o=a?"".concat(a,"/update/internal_user_settings"):"/update/internal_user_settings";console.log("Updating internal user settings:",t);let n=await fetch(o,{method:"PATCH",headers:{[l]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!n.ok){let e=await n.text();throw s(e),Error("Network response was not ok")}let c=await n.json();return console.log("Updated internal user settings:",c),r.ZP.success("Internal user settings updated successfully"),c}catch(e){throw console.error("Failed to update internal user settings:",e),e}}}}]); \ No newline at end of file diff --git a/ui/litellm-dashboard/out/_next/static/chunks/394-0222ddf4d701e0b4.js b/litellm/proxy/_experimental/out/_next/static/chunks/394-48a36e9c9b2cb488.js similarity index 66% rename from ui/litellm-dashboard/out/_next/static/chunks/394-0222ddf4d701e0b4.js rename to litellm/proxy/_experimental/out/_next/static/chunks/394-48a36e9c9b2cb488.js index f6dfcd861e..fa8ff4bc05 100644 --- a/ui/litellm-dashboard/out/_next/static/chunks/394-0222ddf4d701e0b4.js +++ b/litellm/proxy/_experimental/out/_next/static/chunks/394-48a36e9c9b2cb488.js @@ -1,10 +1,10 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[394],{12660:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M917.7 148.8l-42.4-42.4c-1.6-1.6-3.6-2.3-5.7-2.3s-4.1.8-5.7 2.3l-76.1 76.1a199.27 199.27 0 00-112.1-34.3c-51.2 0-102.4 19.5-141.5 58.6L432.3 308.7a8.03 8.03 0 000 11.3L704 591.7c1.6 1.6 3.6 2.3 5.7 2.3 2 0 4.1-.8 5.7-2.3l101.9-101.9c68.9-69 77-175.7 24.3-253.5l76.1-76.1c3.1-3.2 3.1-8.3 0-11.4zM769.1 441.7l-59.4 59.4-186.8-186.8 59.4-59.4c24.9-24.9 58.1-38.7 93.4-38.7 35.3 0 68.4 13.7 93.4 38.7 24.9 24.9 38.7 58.1 38.7 93.4 0 35.3-13.8 68.4-38.7 93.4zm-190.2 105a8.03 8.03 0 00-11.3 0L501 613.3 410.7 523l66.7-66.7c3.1-3.1 3.1-8.2 0-11.3L441 408.6a8.03 8.03 0 00-11.3 0L363 475.3l-43-43a7.85 7.85 0 00-5.7-2.3c-2 0-4.1.8-5.7 2.3L206.8 534.2c-68.9 69-77 175.7-24.3 253.5l-76.1 76.1a8.03 8.03 0 000 11.3l42.4 42.4c1.6 1.6 3.6 2.3 5.7 2.3s4.1-.8 5.7-2.3l76.1-76.1c33.7 22.9 72.9 34.3 112.1 34.3 51.2 0 102.4-19.5 141.5-58.6l101.9-101.9c3.1-3.1 3.1-8.2 0-11.3l-43-43 66.7-66.7c3.1-3.1 3.1-8.2 0-11.3l-36.6-36.2zM441.7 769.1a131.32 131.32 0 01-93.4 38.7c-35.3 0-68.4-13.7-93.4-38.7a131.32 131.32 0 01-38.7-93.4c0-35.3 13.7-68.4 38.7-93.4l59.4-59.4 186.8 186.8-59.4 59.4z"}}]},name:"api",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},88009:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"}}]},name:"appstore",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},37527:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M894 462c30.9 0 43.8-39.7 18.7-58L530.8 126.2a31.81 31.81 0 00-37.6 0L111.3 404c-25.1 18.2-12.2 58 18.8 58H192v374h-72c-4.4 0-8 3.6-8 8v52c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-52c0-4.4-3.6-8-8-8h-72V462h62zM512 196.7l271.1 197.2H240.9L512 196.7zM264 462h117v374H264V462zm189 0h117v374H453V462zm307 374H642V462h118v374z"}}]},name:"bank",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},9775:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M888 792H200V168c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v688c0 4.4 3.6 8 8 8h752c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm-600-80h56c4.4 0 8-3.6 8-8V560c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v144c0 4.4 3.6 8 8 8zm152 0h56c4.4 0 8-3.6 8-8V384c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v320c0 4.4 3.6 8 8 8zm152 0h56c4.4 0 8-3.6 8-8V462c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v242c0 4.4 3.6 8 8 8zm152 0h56c4.4 0 8-3.6 8-8V304c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v400c0 4.4 3.6 8 8 8z"}}]},name:"bar-chart",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},68208:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M856 376H648V168c0-8.8-7.2-16-16-16H168c-8.8 0-16 7.2-16 16v464c0 8.8 7.2 16 16 16h208v208c0 8.8 7.2 16 16 16h464c8.8 0 16-7.2 16-16V392c0-8.8-7.2-16-16-16zm-480 16v188H220V220h360v156H392c-8.8 0-16 7.2-16 16zm204 52v136H444V444h136zm224 360H444V648h188c8.8 0 16-7.2 16-16V444h156v360z"}}]},name:"block",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},9738:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 00-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"}}]},name:"check",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},44625:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V96c0-17.7-14.3-32-32-32zm-600 72h560v208H232V136zm560 480H232V408h560v208zm0 272H232V680h560v208zM304 240a40 40 0 1080 0 40 40 0 10-80 0zm0 272a40 40 0 1080 0 40 40 0 10-80 0zm0 272a40 40 0 1080 0 40 40 0 10-80 0z"}}]},name:"database",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},70464:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"}}]},name:"down",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},73879:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M505.7 661a8 8 0 0012.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"download",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},39760:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M176 511a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0zm280 0a56 56 0 10112 0 56 56 0 10-112 0z"}}]},name:"ellipsis",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},41169:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 472a40 40 0 1080 0 40 40 0 10-80 0zm367 352.9L696.3 352V178H768v-68H256v68h71.7v174L145 824.9c-2.8 7.4-4.3 15.2-4.3 23.1 0 35.3 28.7 64 64 64h614.6c7.9 0 15.7-1.5 23.1-4.3 33-12.7 49.4-49.8 36.6-82.8zM395.7 364.7V180h232.6v184.7L719.2 600c-20.7-5.3-42.1-8-63.9-8-61.2 0-119.2 21.5-165.3 60a188.78 188.78 0 01-121.3 43.9c-32.7 0-64.1-8.3-91.8-23.7l118.8-307.5zM210.5 844l41.7-107.8c35.7 18.1 75.4 27.8 116.6 27.8 61.2 0 119.2-21.5 165.3-60 33.9-28.2 76.3-43.9 121.3-43.9 35 0 68.4 9.5 97.6 27.1L813.5 844h-603z"}}]},name:"experiment",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},6520:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"}}]},name:"eye",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},15424:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M464 336a48 48 0 1096 0 48 48 0 10-96 0zm72 112h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V456c0-4.4-3.6-8-8-8z"}}]},name:"info-circle",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},92403:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M608 112c-167.9 0-304 136.1-304 304 0 70.3 23.9 135 63.9 186.5l-41.1 41.1-62.3-62.3a8.15 8.15 0 00-11.4 0l-39.8 39.8a8.15 8.15 0 000 11.4l62.3 62.3-44.9 44.9-62.3-62.3a8.15 8.15 0 00-11.4 0l-39.8 39.8a8.15 8.15 0 000 11.4l62.3 62.3-65.3 65.3a8.03 8.03 0 000 11.3l42.3 42.3c3.1 3.1 8.2 3.1 11.3 0l253.6-253.6A304.06 304.06 0 00608 720c167.9 0 304-136.1 304-304S775.9 112 608 112zm161.2 465.2C726.2 620.3 668.9 644 608 644c-60.9 0-118.2-23.7-161.2-66.8-43.1-43-66.8-100.3-66.8-161.2 0-60.9 23.7-118.2 66.8-161.2 43-43.1 100.3-66.8 161.2-66.8 60.9 0 118.2 23.7 161.2 66.8 43.1 43 66.8 100.3 66.8 161.2 0 60.9-23.7 118.2-66.8 161.2z"}}]},name:"key",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},15327:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 000 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"}}]},name:"left",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},48231:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M888 792H200V168c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v688c0 4.4 3.6 8 8 8h752c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM305.8 637.7c3.1 3.1 8.1 3.1 11.3 0l138.3-137.6L583 628.5c3.1 3.1 8.2 3.1 11.3 0l275.4-275.3c3.1-3.1 3.1-8.2 0-11.3l-39.6-39.6a8.03 8.03 0 00-11.3 0l-230 229.9L461.4 404a8.03 8.03 0 00-11.3 0L266.3 586.7a8.03 8.03 0 000 11.3l39.5 39.7z"}}]},name:"line-chart",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},40428:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M868 732h-70.3c-4.8 0-9.3 2.1-12.3 5.8-7 8.5-14.5 16.7-22.4 24.5a353.84 353.84 0 01-112.7 75.9A352.8 352.8 0 01512.4 866c-47.9 0-94.3-9.4-137.9-27.8a353.84 353.84 0 01-112.7-75.9 353.28 353.28 0 01-76-112.5C167.3 606.2 158 559.9 158 512s9.4-94.2 27.8-137.8c17.8-42.1 43.4-80 76-112.5s70.5-58.1 112.7-75.9c43.6-18.4 90-27.8 137.9-27.8 47.9 0 94.3 9.3 137.9 27.8 42.2 17.8 80.1 43.4 112.7 75.9 7.9 7.9 15.3 16.1 22.4 24.5 3 3.7 7.6 5.8 12.3 5.8H868c6.3 0 10.2-7 6.7-12.3C798 160.5 663.8 81.6 511.3 82 271.7 82.6 79.6 277.1 82 516.4 84.4 751.9 276.2 942 512.4 942c152.1 0 285.7-78.8 362.3-197.7 3.4-5.3-.4-12.3-6.7-12.3zm88.9-226.3L815 393.7c-5.3-4.2-13-.4-13 6.3v76H488c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h314v76c0 6.7 7.8 10.5 13 6.3l141.9-112a8 8 0 000-12.6z"}}]},name:"logout",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},45246:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M696 480H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h368c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"}},{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}}]},name:"minus-circle",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},28595:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"}},{tag:"path",attrs:{d:"M719.4 499.1l-296.1-215A15.9 15.9 0 00398 297v430c0 13.1 14.8 20.5 25.3 12.9l296.1-215a15.9 15.9 0 000-25.8zm-257.6 134V390.9L628.5 512 461.8 633.1z"}}]},name:"play-circle",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},96473:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"}},{tag:"path",attrs:{d:"M192 474h672q8 0 8 8v60q0 8-8 8H160q-8 0-8-8v-60q0-8 8-8z"}}]},name:"plus",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},57400:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"0 0 1024 1024",focusable:"false"},children:[{tag:"path",attrs:{d:"M512 64L128 192v384c0 212.1 171.9 384 384 384s384-171.9 384-384V192L512 64zm312 512c0 172.3-139.7 312-312 312S200 748.3 200 576V246l312-110 312 110v330z"}},{tag:"path",attrs:{d:"M378.4 475.1a35.91 35.91 0 00-50.9 0 35.91 35.91 0 000 50.9l129.4 129.4 2.1 2.1a33.98 33.98 0 0048.1 0L730.6 434a33.98 33.98 0 000-48.1l-2.8-2.8a33.98 33.98 0 00-48.1 0L483 579.7 378.4 475.1z"}}]},name:"safety",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},29436:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"}}]},name:"search",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},55322:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"}}]},name:"setting",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},41361:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M824.2 699.9a301.55 301.55 0 00-86.4-60.4C783.1 602.8 812 546.8 812 484c0-110.8-92.4-201.7-203.2-200-109.1 1.7-197 90.6-197 200 0 62.8 29 118.8 74.2 155.5a300.95 300.95 0 00-86.4 60.4C345 754.6 314 826.8 312 903.8a8 8 0 008 8.2h56c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5A226.62 226.62 0 01612 684c60.9 0 118.2 23.7 161.3 66.8C814.5 792 838 846.3 840 904.3c.1 4.3 3.7 7.7 8 7.7h56a8 8 0 008-8.2c-2-77-33-149.2-87.8-203.9zM612 612c-34.2 0-66.4-13.3-90.5-37.5a126.86 126.86 0 01-37.5-91.8c.3-32.8 13.4-64.5 36.3-88 24-24.6 56.1-38.3 90.4-38.7 33.9-.3 66.8 12.9 91 36.6 24.8 24.3 38.4 56.8 38.4 91.4 0 34.2-13.3 66.3-37.5 90.5A127.3 127.3 0 01612 612zM361.5 510.4c-.9-8.7-1.4-17.5-1.4-26.4 0-15.9 1.5-31.4 4.3-46.5.7-3.6-1.2-7.3-4.5-8.8-13.6-6.1-26.1-14.5-36.9-25.1a127.54 127.54 0 01-38.7-95.4c.9-32.1 13.8-62.6 36.3-85.6 24.7-25.3 57.9-39.1 93.2-38.7 31.9.3 62.7 12.6 86 34.4 7.9 7.4 14.7 15.6 20.4 24.4 2 3.1 5.9 4.4 9.3 3.2 17.6-6.1 36.2-10.4 55.3-12.4 5.6-.6 8.8-6.6 6.3-11.6-32.5-64.3-98.9-108.7-175.7-109.9-110.9-1.7-203.3 89.2-203.3 199.9 0 62.8 28.9 118.8 74.2 155.5-31.8 14.7-61.1 35-86.5 60.4-54.8 54.7-85.8 126.9-87.8 204a8 8 0 008 8.2h56.1c4.3 0 7.9-3.4 8-7.7 1.9-58 25.4-112.3 66.7-153.5 29.4-29.4 65.4-49.8 104.7-59.7 3.9-1 6.5-4.7 6-8.7z"}}]},name:"team",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},3632:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M400 317.7h73.9V656c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V317.7H624c6.7 0 10.4-7.7 6.3-12.9L518.3 163a8 8 0 00-12.6 0l-112 141.7c-4.1 5.3-.4 13 6.3 13zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z"}}]},name:"upload",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},15883:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M858.5 763.6a374 374 0 00-80.6-119.5 375.63 375.63 0 00-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 00-80.6 119.5A371.7 371.7 0 00136 901.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 008-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"}}]},name:"user",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},35291:function(e,t,n){"use strict";n.d(t,{Z:function(){return l}});var r=n(1119),o=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M464 720a48 48 0 1096 0 48 48 0 10-96 0zm16-304v184c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V416c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8zm475.7 440l-416-720c-6.2-10.7-16.9-16-27.7-16s-21.6 5.3-27.7 16l-416 720C56 877.4 71.4 904 96 904h832c24.6 0 40-26.6 27.7-48zm-783.5-27.9L512 239.9l339.8 588.2H172.2z"}}]},name:"warning",theme:"outlined"},a=n(55015),l=o.forwardRef(function(e,t){return o.createElement(a.Z,(0,r.Z)({},e,{ref:t,icon:i}))})},58747:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var r=n(5853),o=n(2265);let i=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),o.createElement("path",{d:"M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"}))}},4537:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var r=n(5853),o=n(2265);let i=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),o.createElement("path",{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 10.5858L9.17157 7.75736L7.75736 9.17157L10.5858 12L7.75736 14.8284L9.17157 16.2426L12 13.4142L14.8284 16.2426L16.2426 14.8284L13.4142 12L16.2426 9.17157L14.8284 7.75736L12 10.5858Z"}))}},75105:function(e,t,n){"use strict";n.d(t,{Z:function(){return et}});var r=n(5853),o=n(2265),i=n(47625),a=n(93765),l=n(61994),c=n(59221),s=n(86757),u=n.n(s),d=n(95645),f=n.n(d),p=n(77571),h=n.n(p),m=n(82559),g=n.n(m),v=n(21652),y=n.n(v),b=n(57165),x=n(81889),w=n(9841),S=n(58772),k=n(34067),E=n(16630),C=n(85355),O=n(82944),j=["layout","type","stroke","connectNulls","isRange","ref"];function P(e){return(P="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function N(){return(N=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(i,j));return o.createElement(w.m,{clipPath:n?"url(#clipPath-".concat(r,")"):null},o.createElement(b.H,N({},(0,O.L6)(d,!0),{points:e,connectNulls:s,type:l,baseLine:t,layout:a,stroke:"none",className:"recharts-area-area"})),"none"!==c&&o.createElement(b.H,N({},(0,O.L6)(this.props,!1),{className:"recharts-area-curve",layout:a,type:l,connectNulls:s,fill:"none",points:e})),"none"!==c&&u&&o.createElement(b.H,N({},(0,O.L6)(this.props,!1),{className:"recharts-area-curve",layout:a,type:l,connectNulls:s,fill:"none",points:t})))}},{key:"renderAreaWithAnimation",value:function(e,t){var n=this,r=this.props,i=r.points,a=r.baseLine,l=r.isAnimationActive,s=r.animationBegin,u=r.animationDuration,d=r.animationEasing,f=r.animationId,p=this.state,m=p.prevPoints,v=p.prevBaseLine;return o.createElement(c.ZP,{begin:s,duration:u,isActive:l,easing:d,from:{t:0},to:{t:1},key:"area-".concat(f),onAnimationEnd:this.handleAnimationEnd,onAnimationStart:this.handleAnimationStart},function(r){var l=r.t;if(m){var c,s=m.length/i.length,u=i.map(function(e,t){var n=Math.floor(t*s);if(m[n]){var r=m[n],o=(0,E.k4)(r.x,e.x),i=(0,E.k4)(r.y,e.y);return I(I({},e),{},{x:o(l),y:i(l)})}return e});return c=(0,E.hj)(a)&&"number"==typeof a?(0,E.k4)(v,a)(l):h()(a)||g()(a)?(0,E.k4)(v,0)(l):a.map(function(e,t){var n=Math.floor(t*s);if(v[n]){var r=v[n],o=(0,E.k4)(r.x,e.x),i=(0,E.k4)(r.y,e.y);return I(I({},e),{},{x:o(l),y:i(l)})}return e}),n.renderAreaStatically(u,c,e,t)}return o.createElement(w.m,null,o.createElement("defs",null,o.createElement("clipPath",{id:"animationClipPath-".concat(t)},n.renderClipRect(l))),o.createElement(w.m,{clipPath:"url(#animationClipPath-".concat(t,")")},n.renderAreaStatically(i,a,e,t)))})}},{key:"renderArea",value:function(e,t){var n=this.props,r=n.points,o=n.baseLine,i=n.isAnimationActive,a=this.state,l=a.prevPoints,c=a.prevBaseLine,s=a.totalLength;return i&&r&&r.length&&(!l&&s>0||!y()(l,r)||!y()(c,o))?this.renderAreaWithAnimation(e,t):this.renderAreaStatically(r,o,e,t)}},{key:"render",value:function(){var e,t=this.props,n=t.hide,r=t.dot,i=t.points,a=t.className,c=t.top,s=t.left,u=t.xAxis,d=t.yAxis,f=t.width,p=t.height,m=t.isAnimationActive,g=t.id;if(n||!i||!i.length)return null;var v=this.state.isAnimationFinished,y=1===i.length,b=(0,l.Z)("recharts-area",a),x=u&&u.allowDataOverflow,k=d&&d.allowDataOverflow,E=x||k,C=h()(g)?this.id:g,j=null!==(e=(0,O.L6)(r,!1))&&void 0!==e?e:{r:3,strokeWidth:2},P=j.r,N=j.strokeWidth,M=((0,O.$k)(r)?r:{}).clipDot,I=void 0===M||M,R=2*(void 0===P?3:P)+(void 0===N?2:N);return o.createElement(w.m,{className:b},x||k?o.createElement("defs",null,o.createElement("clipPath",{id:"clipPath-".concat(C)},o.createElement("rect",{x:x?s:s-f/2,y:k?c:c-p/2,width:x?f:2*f,height:k?p:2*p})),!I&&o.createElement("clipPath",{id:"clipPath-dots-".concat(C)},o.createElement("rect",{x:s-R/2,y:c-R/2,width:f+R,height:p+R}))):null,y?null:this.renderArea(E,C),(r||y)&&this.renderDots(E,I,C),(!m||v)&&S.e.renderCallByParent(this.props,i))}}],r=[{key:"getDerivedStateFromProps",value:function(e,t){return e.animationId!==t.prevAnimationId?{prevAnimationId:e.animationId,curPoints:e.points,curBaseLine:e.baseLine,prevPoints:t.curPoints,prevBaseLine:t.curBaseLine}:e.points!==t.curPoints||e.baseLine!==t.curBaseLine?{curPoints:e.points,curBaseLine:e.baseLine}:null}}],n&&R(a.prototype,n),r&&R(a,r),Object.defineProperty(a,"prototype",{writable:!1}),a}(o.PureComponent);D(L,"displayName","Area"),D(L,"defaultProps",{stroke:"#3182bd",fill:"#3182bd",fillOpacity:.6,xAxisId:0,yAxisId:0,legendType:"line",connectNulls:!1,points:[],dot:!1,activeDot:!0,hide:!1,isAnimationActive:!k.x.isSsr,animationBegin:0,animationDuration:1500,animationEasing:"ease"}),D(L,"getBaseValue",function(e,t,n,r){var o=e.layout,i=e.baseValue,a=t.props.baseValue,l=null!=a?a:i;if((0,E.hj)(l)&&"number"==typeof l)return l;var c="horizontal"===o?r:n,s=c.scale.domain();if("number"===c.type){var u=Math.max(s[0],s[1]),d=Math.min(s[0],s[1]);return"dataMin"===l?d:"dataMax"===l?u:u<0?u:Math.max(Math.min(s[0],s[1]),0)}return"dataMin"===l?s[0]:"dataMax"===l?s[1]:s[0]}),D(L,"getComposedData",function(e){var t,n=e.props,r=e.item,o=e.xAxis,i=e.yAxis,a=e.xAxisTicks,l=e.yAxisTicks,c=e.bandSize,s=e.dataKey,u=e.stackedData,d=e.dataStartIndex,f=e.displayedData,p=e.offset,h=n.layout,m=u&&u.length,g=L.getBaseValue(n,r,o,i),v="horizontal"===h,y=!1,b=f.map(function(e,t){m?n=u[d+t]:Array.isArray(n=(0,C.F$)(e,s))?y=!0:n=[g,n];var n,r=null==n[1]||m&&null==(0,C.F$)(e,s);return v?{x:(0,C.Hv)({axis:o,ticks:a,bandSize:c,entry:e,index:t}),y:r?null:i.scale(n[1]),value:n,payload:e}:{x:r?null:o.scale(n[1]),y:(0,C.Hv)({axis:i,ticks:l,bandSize:c,entry:e,index:t}),value:n,payload:e}});return t=m||y?b.map(function(e){var t=Array.isArray(e.value)?e.value[0]:null;return v?{x:e.x,y:null!=t&&null!=e.y?i.scale(t):null}:{x:null!=t?o.scale(t):null,y:e.y}}):v?i.scale(g):o.scale(g),I({points:b,baseLine:t,layout:h,isRange:y},p)}),D(L,"renderDotItem",function(e,t){return o.isValidElement(e)?o.cloneElement(e,t):u()(e)?e(t):o.createElement(x.o,N({},t,{className:"recharts-area-dot"}))});var z=n(97059),B=n(62994),F=n(25311),H=(0,a.z)({chartName:"AreaChart",GraphicalChild:L,axisComponents:[{axisType:"xAxis",AxisComp:z.K},{axisType:"yAxis",AxisComp:B.B}],formatAxisMap:F.t9}),q=n(56940),W=n(8147),K=n(22190),V=n(54061),U=n(65278),G=n(98593),X=n(69448),$=n(32644),Y=n(7084),Q=n(26898),J=n(65954),ee=n(1153);let et=o.forwardRef((e,t)=>{let{data:n=[],categories:a=[],index:l,stack:c=!1,colors:s=Q.s,valueFormatter:u=ee.Cj,startEndOnly:d=!1,showXAxis:f=!0,showYAxis:p=!0,yAxisWidth:h=56,intervalType:m="equidistantPreserveStart",showAnimation:g=!1,animationDuration:v=900,showTooltip:y=!0,showLegend:b=!0,showGridLines:w=!0,showGradient:S=!0,autoMinValue:k=!1,curveType:E="linear",minValue:C,maxValue:O,connectNulls:j=!1,allowDecimals:P=!0,noDataText:N,className:M,onValueChange:I,enableLegendSlider:R=!1,customTooltip:T,rotateLabelX:A,tickGap:_=5}=e,D=(0,r._T)(e,["data","categories","index","stack","colors","valueFormatter","startEndOnly","showXAxis","showYAxis","yAxisWidth","intervalType","showAnimation","animationDuration","showTooltip","showLegend","showGridLines","showGradient","autoMinValue","curveType","minValue","maxValue","connectNulls","allowDecimals","noDataText","className","onValueChange","enableLegendSlider","customTooltip","rotateLabelX","tickGap"]),Z=(f||p)&&(!d||p)?20:0,[F,et]=(0,o.useState)(60),[en,er]=(0,o.useState)(void 0),[eo,ei]=(0,o.useState)(void 0),ea=(0,$.me)(a,s),el=(0,$.i4)(k,C,O),ec=!!I;function es(e){ec&&(e===eo&&!en||(0,$.FB)(n,e)&&en&&en.dataKey===e?(ei(void 0),null==I||I(null)):(ei(e),null==I||I({eventType:"category",categoryClicked:e})),er(void 0))}return o.createElement("div",Object.assign({ref:t,className:(0,J.q)("w-full h-80",M)},D),o.createElement(i.h,{className:"h-full w-full"},(null==n?void 0:n.length)?o.createElement(H,{data:n,onClick:ec&&(eo||en)?()=>{er(void 0),ei(void 0),null==I||I(null)}:void 0},w?o.createElement(q.q,{className:(0,J.q)("stroke-1","stroke-tremor-border","dark:stroke-dark-tremor-border"),horizontal:!0,vertical:!1}):null,o.createElement(z.K,{padding:{left:Z,right:Z},hide:!f,dataKey:l,tick:{transform:"translate(0, 6)"},ticks:d?[n[0][l],n[n.length-1][l]]:void 0,fill:"",stroke:"",className:(0,J.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),interval:d?"preserveStartEnd":m,tickLine:!1,axisLine:!1,minTickGap:_,angle:null==A?void 0:A.angle,dy:null==A?void 0:A.verticalShift,height:null==A?void 0:A.xAxisHeight}),o.createElement(B.B,{width:h,hide:!p,axisLine:!1,tickLine:!1,type:"number",domain:el,tick:{transform:"translate(-3, 0)"},fill:"",stroke:"",className:(0,J.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickFormatter:u,allowDecimals:P}),o.createElement(W.u,{wrapperStyle:{outline:"none"},isAnimationActive:!1,cursor:{stroke:"#d1d5db",strokeWidth:1},content:y?e=>{let{active:t,payload:n,label:r}=e;return T?o.createElement(T,{payload:null==n?void 0:n.map(e=>{var t;return Object.assign(Object.assign({},e),{color:null!==(t=ea.get(e.dataKey))&&void 0!==t?t:Y.fr.Gray})}),active:t,label:r}):o.createElement(G.ZP,{active:t,payload:n,label:r,valueFormatter:u,categoryColors:ea})}:o.createElement(o.Fragment,null),position:{y:0}}),b?o.createElement(K.D,{verticalAlign:"top",height:F,content:e=>{let{payload:t}=e;return(0,U.Z)({payload:t},ea,et,eo,ec?e=>es(e):void 0,R)}}):null,a.map(e=>{var t,n;return o.createElement("defs",{key:e},S?o.createElement("linearGradient",{className:(0,ee.bM)(null!==(t=ea.get(e))&&void 0!==t?t:Y.fr.Gray,Q.K.text).textColor,id:ea.get(e),x1:"0",y1:"0",x2:"0",y2:"1"},o.createElement("stop",{offset:"5%",stopColor:"currentColor",stopOpacity:en||eo&&eo!==e?.15:.4}),o.createElement("stop",{offset:"95%",stopColor:"currentColor",stopOpacity:0})):o.createElement("linearGradient",{className:(0,ee.bM)(null!==(n=ea.get(e))&&void 0!==n?n:Y.fr.Gray,Q.K.text).textColor,id:ea.get(e),x1:"0",y1:"0",x2:"0",y2:"1"},o.createElement("stop",{stopColor:"currentColor",stopOpacity:en||eo&&eo!==e?.1:.3})))}),a.map(e=>{var t;return o.createElement(L,{className:(0,ee.bM)(null!==(t=ea.get(e))&&void 0!==t?t:Y.fr.Gray,Q.K.text).strokeColor,strokeOpacity:en||eo&&eo!==e?.3:1,activeDot:e=>{var t;let{cx:r,cy:i,stroke:a,strokeLinecap:l,strokeLinejoin:c,strokeWidth:s,dataKey:u}=e;return o.createElement(x.o,{className:(0,J.q)("stroke-tremor-background dark:stroke-dark-tremor-background",I?"cursor-pointer":"",(0,ee.bM)(null!==(t=ea.get(u))&&void 0!==t?t:Y.fr.Gray,Q.K.text).fillColor),cx:r,cy:i,r:5,fill:"",stroke:a,strokeLinecap:l,strokeLinejoin:c,strokeWidth:s,onClick:(t,r)=>{r.stopPropagation(),ec&&(e.index===(null==en?void 0:en.index)&&e.dataKey===(null==en?void 0:en.dataKey)||(0,$.FB)(n,e.dataKey)&&eo&&eo===e.dataKey?(ei(void 0),er(void 0),null==I||I(null)):(ei(e.dataKey),er({index:e.index,dataKey:e.dataKey}),null==I||I(Object.assign({eventType:"dot",categoryClicked:e.dataKey},e.payload))))}})},dot:t=>{var r;let{stroke:i,strokeLinecap:a,strokeLinejoin:l,strokeWidth:c,cx:s,cy:u,dataKey:d,index:f}=t;return(0,$.FB)(n,e)&&!(en||eo&&eo!==e)||(null==en?void 0:en.index)===f&&(null==en?void 0:en.dataKey)===e?o.createElement(x.o,{key:f,cx:s,cy:u,r:5,stroke:i,fill:"",strokeLinecap:a,strokeLinejoin:l,strokeWidth:c,className:(0,J.q)("stroke-tremor-background dark:stroke-dark-tremor-background",I?"cursor-pointer":"",(0,ee.bM)(null!==(r=ea.get(d))&&void 0!==r?r:Y.fr.Gray,Q.K.text).fillColor)}):o.createElement(o.Fragment,{key:f})},key:e,name:e,type:E,dataKey:e,stroke:"",fill:"url(#".concat(ea.get(e),")"),strokeWidth:2,strokeLinejoin:"round",strokeLinecap:"round",isAnimationActive:g,animationDuration:v,stackId:c?"a":void 0,connectNulls:j})}),I?a.map(e=>o.createElement(V.x,{className:(0,J.q)("cursor-pointer"),strokeOpacity:0,key:e,name:e,type:E,dataKey:e,stroke:"transparent",fill:"transparent",legendType:"none",tooltipType:"none",strokeWidth:12,connectNulls:j,onClick:(e,t)=>{t.stopPropagation();let{name:n}=e;es(n)}})):null):o.createElement(X.Z,{noDataText:N})))});et.displayName="AreaChart"},40278:function(e,t,n){"use strict";n.d(t,{Z:function(){return k}});var r=n(5853),o=n(7084),i=n(26898),a=n(65954),l=n(1153),c=n(2265),s=n(47625),u=n(93765),d=n(31699),f=n(97059),p=n(62994),h=n(25311),m=(0,u.z)({chartName:"BarChart",GraphicalChild:d.$,defaultTooltipEventType:"axis",validateTooltipEventTypes:["axis","item"],axisComponents:[{axisType:"xAxis",AxisComp:f.K},{axisType:"yAxis",AxisComp:p.B}],formatAxisMap:h.t9}),g=n(56940),v=n(8147),y=n(22190),b=n(65278),x=n(98593),w=n(69448),S=n(32644);let k=c.forwardRef((e,t)=>{let{data:n=[],categories:u=[],index:h,colors:k=i.s,valueFormatter:E=l.Cj,layout:C="horizontal",stack:O=!1,relative:j=!1,startEndOnly:P=!1,animationDuration:N=900,showAnimation:M=!1,showXAxis:I=!0,showYAxis:R=!0,yAxisWidth:T=56,intervalType:A="equidistantPreserveStart",showTooltip:_=!0,showLegend:D=!0,showGridLines:Z=!0,autoMinValue:L=!1,minValue:z,maxValue:B,allowDecimals:F=!0,noDataText:H,onValueChange:q,enableLegendSlider:W=!1,customTooltip:K,rotateLabelX:V,tickGap:U=5,className:G}=e,X=(0,r._T)(e,["data","categories","index","colors","valueFormatter","layout","stack","relative","startEndOnly","animationDuration","showAnimation","showXAxis","showYAxis","yAxisWidth","intervalType","showTooltip","showLegend","showGridLines","autoMinValue","minValue","maxValue","allowDecimals","noDataText","onValueChange","enableLegendSlider","customTooltip","rotateLabelX","tickGap","className"]),$=I||R?20:0,[Y,Q]=(0,c.useState)(60),J=(0,S.me)(u,k),[ee,et]=c.useState(void 0),[en,er]=(0,c.useState)(void 0),eo=!!q;function ei(e,t,n){var r,o,i,a;n.stopPropagation(),q&&((0,S.vZ)(ee,Object.assign(Object.assign({},e.payload),{value:e.value}))?(er(void 0),et(void 0),null==q||q(null)):(er(null===(o=null===(r=e.tooltipPayload)||void 0===r?void 0:r[0])||void 0===o?void 0:o.dataKey),et(Object.assign(Object.assign({},e.payload),{value:e.value})),null==q||q(Object.assign({eventType:"bar",categoryClicked:null===(a=null===(i=e.tooltipPayload)||void 0===i?void 0:i[0])||void 0===a?void 0:a.dataKey},e.payload))))}let ea=(0,S.i4)(L,z,B);return c.createElement("div",Object.assign({ref:t,className:(0,a.q)("w-full h-80",G)},X),c.createElement(s.h,{className:"h-full w-full"},(null==n?void 0:n.length)?c.createElement(m,{data:n,stackOffset:O?"sign":j?"expand":"none",layout:"vertical"===C?"vertical":"horizontal",onClick:eo&&(en||ee)?()=>{et(void 0),er(void 0),null==q||q(null)}:void 0},Z?c.createElement(g.q,{className:(0,a.q)("stroke-1","stroke-tremor-border","dark:stroke-dark-tremor-border"),horizontal:"vertical"!==C,vertical:"vertical"===C}):null,"vertical"!==C?c.createElement(f.K,{padding:{left:$,right:$},hide:!I,dataKey:h,interval:P?"preserveStartEnd":A,tick:{transform:"translate(0, 6)"},ticks:P?[n[0][h],n[n.length-1][h]]:void 0,fill:"",stroke:"",className:(0,a.q)("mt-4 text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickLine:!1,axisLine:!1,angle:null==V?void 0:V.angle,dy:null==V?void 0:V.verticalShift,height:null==V?void 0:V.xAxisHeight,minTickGap:U}):c.createElement(f.K,{hide:!I,type:"number",tick:{transform:"translate(-3, 0)"},domain:ea,fill:"",stroke:"",className:(0,a.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickLine:!1,axisLine:!1,tickFormatter:E,minTickGap:U,allowDecimals:F,angle:null==V?void 0:V.angle,dy:null==V?void 0:V.verticalShift,height:null==V?void 0:V.xAxisHeight}),"vertical"!==C?c.createElement(p.B,{width:T,hide:!R,axisLine:!1,tickLine:!1,type:"number",domain:ea,tick:{transform:"translate(-3, 0)"},fill:"",stroke:"",className:(0,a.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickFormatter:j?e=>"".concat((100*e).toString()," %"):E,allowDecimals:F}):c.createElement(p.B,{width:T,hide:!R,dataKey:h,axisLine:!1,tickLine:!1,ticks:P?[n[0][h],n[n.length-1][h]]:void 0,type:"category",interval:"preserveStartEnd",tick:{transform:"translate(0, 6)"},fill:"",stroke:"",className:(0,a.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content")}),c.createElement(v.u,{wrapperStyle:{outline:"none"},isAnimationActive:!1,cursor:{fill:"#d1d5db",opacity:"0.15"},content:_?e=>{let{active:t,payload:n,label:r}=e;return K?c.createElement(K,{payload:null==n?void 0:n.map(e=>{var t;return Object.assign(Object.assign({},e),{color:null!==(t=J.get(e.dataKey))&&void 0!==t?t:o.fr.Gray})}),active:t,label:r}):c.createElement(x.ZP,{active:t,payload:n,label:r,valueFormatter:E,categoryColors:J})}:c.createElement(c.Fragment,null),position:{y:0}}),D?c.createElement(y.D,{verticalAlign:"top",height:Y,content:e=>{let{payload:t}=e;return(0,b.Z)({payload:t},J,Q,en,eo?e=>{eo&&(e!==en||ee?(er(e),null==q||q({eventType:"category",categoryClicked:e})):(er(void 0),null==q||q(null)),et(void 0))}:void 0,W)}}):null,u.map(e=>{var t;return c.createElement(d.$,{className:(0,a.q)((0,l.bM)(null!==(t=J.get(e))&&void 0!==t?t:o.fr.Gray,i.K.background).fillColor,q?"cursor-pointer":""),key:e,name:e,type:"linear",stackId:O||j?"a":void 0,dataKey:e,fill:"",isAnimationActive:M,animationDuration:N,shape:e=>((e,t,n,r)=>{let{fillOpacity:o,name:i,payload:a,value:l}=e,{x:s,width:u,y:d,height:f}=e;return"horizontal"===r&&f<0?(d+=f,f=Math.abs(f)):"vertical"===r&&u<0&&(s+=u,u=Math.abs(u)),c.createElement("rect",{x:s,y:d,width:u,height:f,opacity:t||n&&n!==i?(0,S.vZ)(t,Object.assign(Object.assign({},a),{value:l}))?o:.3:o})})(e,ee,en,C),onClick:ei})})):c.createElement(w.Z,{noDataText:H})))});k.displayName="BarChart"},14042:function(e,t,n){"use strict";n.d(t,{Z:function(){return ez}});var r=n(5853),o=n(7084),i=n(26898),a=n(65954),l=n(1153),c=n(2265),s=n(60474),u=n(47625),d=n(93765),f=n(86757),p=n.n(f),h=n(9841),m=n(81889),g=n(61994),v=n(82944),y=["points","className","baseLinePoints","connectNulls"];function b(){return(b=Object.assign?Object.assign.bind():function(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=Array(t);n0&&void 0!==arguments[0]?arguments[0]:[],t=[[]];return e.forEach(function(e){S(e)?t[t.length-1].push(e):t[t.length-1].length>0&&t.push([])}),S(e[0])&&t[t.length-1].push(e[0]),t[t.length-1].length<=0&&(t=t.slice(0,-1)),t},E=function(e,t){var n=k(e);t&&(n=[n.reduce(function(e,t){return[].concat(x(e),x(t))},[])]);var r=n.map(function(e){return e.reduce(function(e,t,n){return"".concat(e).concat(0===n?"M":"L").concat(t.x,",").concat(t.y)},"")}).join("");return 1===n.length?"".concat(r,"Z"):r},C=function(e,t,n){var r=E(e,n);return"".concat("Z"===r.slice(-1)?r.slice(0,-1):r,"L").concat(E(t.reverse(),n).slice(1))},O=function(e){var t=e.points,n=e.className,r=e.baseLinePoints,o=e.connectNulls,i=function(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},i=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,y);if(!t||!t.length)return null;var a=(0,g.Z)("recharts-polygon",n);if(r&&r.length){var l=i.stroke&&"none"!==i.stroke,s=C(t,r,o);return c.createElement("g",{className:a},c.createElement("path",b({},(0,v.L6)(i,!0),{fill:"Z"===s.slice(-1)?i.fill:"none",stroke:"none",d:s})),l?c.createElement("path",b({},(0,v.L6)(i,!0),{fill:"none",d:E(t,o)})):null,l?c.createElement("path",b({},(0,v.L6)(i,!0),{fill:"none",d:E(r,o)})):null)}var u=E(t,o);return c.createElement("path",b({},(0,v.L6)(i,!0),{fill:"Z"===u.slice(-1)?i.fill:"none",className:a,d:u}))},j=n(58811),P=n(41637),N=n(39206);function M(e){return(M="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function I(){return(I=Object.assign?Object.assign.bind():function(e){for(var t=1;t1e-5?"outer"===t?"start":"end":n<-.00001?"outer"===t?"end":"start":"middle"}},{key:"renderAxisLine",value:function(){var e=this.props,t=e.cx,n=e.cy,r=e.radius,o=e.axisLine,i=e.axisLineType,a=T(T({},(0,v.L6)(this.props,!1)),{},{fill:"none"},(0,v.L6)(o,!1));if("circle"===i)return c.createElement(m.o,I({className:"recharts-polar-angle-axis-line"},a,{cx:t,cy:n,r:r}));var l=this.props.ticks.map(function(e){return(0,N.op)(t,n,r,e.coordinate)});return c.createElement(O,I({className:"recharts-polar-angle-axis-line"},a,{points:l}))}},{key:"renderTicks",value:function(){var e=this,t=this.props,n=t.ticks,r=t.tick,o=t.tickLine,a=t.tickFormatter,l=t.stroke,s=(0,v.L6)(this.props,!1),u=(0,v.L6)(r,!1),d=T(T({},s),{},{fill:"none"},(0,v.L6)(o,!1)),f=n.map(function(t,n){var f=e.getTickLineCoord(t),p=T(T(T({textAnchor:e.getTickTextAnchor(t)},s),{},{stroke:"none",fill:l},u),{},{index:n,payload:t,x:f.x2,y:f.y2});return c.createElement(h.m,I({className:"recharts-polar-angle-axis-tick",key:"tick-".concat(t.coordinate)},(0,P.bw)(e.props,t,n)),o&&c.createElement("line",I({className:"recharts-polar-angle-axis-tick-line"},d,f)),r&&i.renderTickItem(r,p,a?a(t.value,n):t.value))});return c.createElement(h.m,{className:"recharts-polar-angle-axis-ticks"},f)}},{key:"render",value:function(){var e=this.props,t=e.ticks,n=e.radius,r=e.axisLine;return!(n<=0)&&t&&t.length?c.createElement(h.m,{className:"recharts-polar-angle-axis"},r&&this.renderAxisLine(),this.renderTicks()):null}}],r=[{key:"renderTickItem",value:function(e,t,n){return c.isValidElement(e)?c.cloneElement(e,t):p()(e)?e(t):c.createElement(j.x,I({},t,{className:"recharts-polar-angle-axis-tick-value"}),n)}}],n&&A(i.prototype,n),r&&A(i,r),Object.defineProperty(i,"prototype",{writable:!1}),i}(c.PureComponent);Z(B,"displayName","PolarAngleAxis"),Z(B,"axisType","angleAxis"),Z(B,"defaultProps",{type:"category",angleAxisId:0,scale:"auto",cx:0,cy:0,orientation:"outer",axisLine:!0,tickLine:!0,tickSize:8,tick:!0,hide:!1,allowDuplicatedCategory:!0});var F=n(35802),H=n.n(F),q=n(37891),W=n.n(q),K=n(26680),V=["cx","cy","angle","ticks","axisLine"],U=["ticks","tick","angle","tickFormatter","stroke"];function G(e){return(G="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function X(){return(X=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0)&&Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function J(e,t){for(var n=0;n0?el()(e,"paddingAngle",0):0;if(n){var l=(0,eg.k4)(n.endAngle-n.startAngle,e.endAngle-e.startAngle),c=ek(ek({},e),{},{startAngle:i+a,endAngle:i+l(r)+a});o.push(c),i=c.endAngle}else{var s=e.endAngle,d=e.startAngle,f=(0,eg.k4)(0,s-d)(r),p=ek(ek({},e),{},{startAngle:i+a,endAngle:i+f+a});o.push(p),i=p.endAngle}}),c.createElement(h.m,null,e.renderSectorsStatically(o))})}},{key:"attachKeyboardHandlers",value:function(e){var t=this;e.onkeydown=function(e){if(!e.altKey)switch(e.key){case"ArrowLeft":var n=++t.state.sectorToFocus%t.sectorRefs.length;t.sectorRefs[n].focus(),t.setState({sectorToFocus:n});break;case"ArrowRight":var r=--t.state.sectorToFocus<0?t.sectorRefs.length-1:t.state.sectorToFocus%t.sectorRefs.length;t.sectorRefs[r].focus(),t.setState({sectorToFocus:r});break;case"Escape":t.sectorRefs[t.state.sectorToFocus].blur(),t.setState({sectorToFocus:0})}}}},{key:"renderSectors",value:function(){var e=this.props,t=e.sectors,n=e.isAnimationActive,r=this.state.prevSectors;return n&&t&&t.length&&(!r||!es()(r,t))?this.renderSectorsWithAnimation():this.renderSectorsStatically(t)}},{key:"componentDidMount",value:function(){this.pieRef&&this.attachKeyboardHandlers(this.pieRef)}},{key:"render",value:function(){var e=this,t=this.props,n=t.hide,r=t.sectors,o=t.className,i=t.label,a=t.cx,l=t.cy,s=t.innerRadius,u=t.outerRadius,d=t.isAnimationActive,f=this.state.isAnimationFinished;if(n||!r||!r.length||!(0,eg.hj)(a)||!(0,eg.hj)(l)||!(0,eg.hj)(s)||!(0,eg.hj)(u))return null;var p=(0,g.Z)("recharts-pie",o);return c.createElement(h.m,{tabIndex:this.props.rootTabIndex,className:p,ref:function(t){e.pieRef=t}},this.renderSectors(),i&&this.renderLabels(r),K._.renderCallByParent(this.props,null,!1),(!d||f)&&ep.e.renderCallByParent(this.props,r,!1))}}],r=[{key:"getDerivedStateFromProps",value:function(e,t){return t.prevIsAnimationActive!==e.isAnimationActive?{prevIsAnimationActive:e.isAnimationActive,prevAnimationId:e.animationId,curSectors:e.sectors,prevSectors:[],isAnimationFinished:!0}:e.isAnimationActive&&e.animationId!==t.prevAnimationId?{prevAnimationId:e.animationId,curSectors:e.sectors,prevSectors:t.curSectors,isAnimationFinished:!0}:e.sectors!==t.curSectors?{curSectors:e.sectors,isAnimationFinished:!0}:null}},{key:"getTextAnchor",value:function(e,t){return e>t?"start":e=360?x:x-1)*u,S=i.reduce(function(e,t){var n=(0,ev.F$)(t,b,0);return e+((0,eg.hj)(n)?n:0)},0);return S>0&&(t=i.map(function(e,t){var r,o=(0,ev.F$)(e,b,0),i=(0,ev.F$)(e,f,t),a=((0,eg.hj)(o)?o:0)/S,s=(r=t?n.endAngle+(0,eg.uY)(v)*u*(0!==o?1:0):c)+(0,eg.uY)(v)*((0!==o?m:0)+a*w),d=(r+s)/2,p=(g.innerRadius+g.outerRadius)/2,y=[{name:i,value:o,payload:e,dataKey:b,type:h}],x=(0,N.op)(g.cx,g.cy,p,d);return n=ek(ek(ek({percent:a,cornerRadius:l,name:i,tooltipPayload:y,midAngle:d,middleRadius:p,tooltipPosition:x},e),g),{},{value:(0,ev.F$)(e,b),startAngle:r,endAngle:s,payload:e,paddingAngle:(0,eg.uY)(v)*u})})),ek(ek({},g),{},{sectors:t,data:i})});var eI=(0,d.z)({chartName:"PieChart",GraphicalChild:eM,validateTooltipEventTypes:["item"],defaultTooltipEventType:"item",legendContent:"children",axisComponents:[{axisType:"angleAxis",AxisComp:B},{axisType:"radiusAxis",AxisComp:eo}],formatAxisMap:N.t9,defaultProps:{layout:"centric",startAngle:0,endAngle:360,cx:"50%",cy:"50%",innerRadius:0,outerRadius:"80%"}}),eR=n(8147),eT=n(69448),eA=n(98593);let e_=e=>{let{active:t,payload:n,valueFormatter:r}=e;if(t&&(null==n?void 0:n[0])){let e=null==n?void 0:n[0];return c.createElement(eA.$B,null,c.createElement("div",{className:(0,a.q)("px-4 py-2")},c.createElement(eA.zX,{value:r(e.value),name:e.name,color:e.payload.color})))}return null},eD=(e,t)=>e.map((e,n)=>{let r=ne||t((0,l.vP)(n.map(e=>e[r]))),eL=e=>{let{cx:t,cy:n,innerRadius:r,outerRadius:o,startAngle:i,endAngle:a,className:l}=e;return c.createElement("g",null,c.createElement(s.L,{cx:t,cy:n,innerRadius:r,outerRadius:o,startAngle:i,endAngle:a,className:l,fill:"",opacity:.3,style:{outline:"none"}}))},ez=c.forwardRef((e,t)=>{let{data:n=[],category:s="value",index:d="name",colors:f=i.s,variant:p="donut",valueFormatter:h=l.Cj,label:m,showLabel:g=!0,animationDuration:v=900,showAnimation:y=!1,showTooltip:b=!0,noDataText:x,onValueChange:w,customTooltip:S,className:k}=e,E=(0,r._T)(e,["data","category","index","colors","variant","valueFormatter","label","showLabel","animationDuration","showAnimation","showTooltip","noDataText","onValueChange","customTooltip","className"]),C="donut"==p,O=eZ(m,h,n,s),[j,P]=c.useState(void 0),N=!!w;return(0,c.useEffect)(()=>{let e=document.querySelectorAll(".recharts-pie-sector");e&&e.forEach(e=>{e.setAttribute("style","outline: none")})},[j]),c.createElement("div",Object.assign({ref:t,className:(0,a.q)("w-full h-40",k)},E),c.createElement(u.h,{className:"h-full w-full"},(null==n?void 0:n.length)?c.createElement(eI,{onClick:N&&j?()=>{P(void 0),null==w||w(null)}:void 0,margin:{top:0,left:0,right:0,bottom:0}},g&&C?c.createElement("text",{className:(0,a.q)("fill-tremor-content-emphasis","dark:fill-dark-tremor-content-emphasis"),x:"50%",y:"50%",textAnchor:"middle",dominantBaseline:"middle"},O):null,c.createElement(eM,{className:(0,a.q)("stroke-tremor-background dark:stroke-dark-tremor-background",w?"cursor-pointer":"cursor-default"),data:eD(n,f),cx:"50%",cy:"50%",startAngle:90,endAngle:-270,innerRadius:C?"75%":"0%",outerRadius:"100%",stroke:"",strokeLinejoin:"round",dataKey:s,nameKey:d,isAnimationActive:y,animationDuration:v,onClick:function(e,t,n){n.stopPropagation(),N&&(j===t?(P(void 0),null==w||w(null)):(P(t),null==w||w(Object.assign({eventType:"slice"},e.payload.payload))))},activeIndex:j,inactiveShape:eL,style:{outline:"none"}}),c.createElement(eR.u,{wrapperStyle:{outline:"none"},isAnimationActive:!1,content:b?e=>{var t;let{active:n,payload:r}=e;return S?c.createElement(S,{payload:null==r?void 0:r.map(e=>{var t,n,i;return Object.assign(Object.assign({},e),{color:null!==(i=null===(n=null===(t=null==r?void 0:r[0])||void 0===t?void 0:t.payload)||void 0===n?void 0:n.color)&&void 0!==i?i:o.fr.Gray})}),active:n,label:null===(t=null==r?void 0:r[0])||void 0===t?void 0:t.name}):c.createElement(e_,{active:n,payload:r,valueFormatter:h})}:c.createElement(c.Fragment,null)})):c.createElement(eT.Z,{noDataText:x})))});ez.displayName="DonutChart"},59664:function(e,t,n){"use strict";n.d(t,{Z:function(){return E}});var r=n(5853),o=n(2265),i=n(47625),a=n(93765),l=n(54061),c=n(97059),s=n(62994),u=n(25311),d=(0,a.z)({chartName:"LineChart",GraphicalChild:l.x,axisComponents:[{axisType:"xAxis",AxisComp:c.K},{axisType:"yAxis",AxisComp:s.B}],formatAxisMap:u.t9}),f=n(56940),p=n(8147),h=n(22190),m=n(81889),g=n(65278),v=n(98593),y=n(69448),b=n(32644),x=n(7084),w=n(26898),S=n(65954),k=n(1153);let E=o.forwardRef((e,t)=>{let{data:n=[],categories:a=[],index:u,colors:E=w.s,valueFormatter:C=k.Cj,startEndOnly:O=!1,showXAxis:j=!0,showYAxis:P=!0,yAxisWidth:N=56,intervalType:M="equidistantPreserveStart",animationDuration:I=900,showAnimation:R=!1,showTooltip:T=!0,showLegend:A=!0,showGridLines:_=!0,autoMinValue:D=!1,curveType:Z="linear",minValue:L,maxValue:z,connectNulls:B=!1,allowDecimals:F=!0,noDataText:H,className:q,onValueChange:W,enableLegendSlider:K=!1,customTooltip:V,rotateLabelX:U,tickGap:G=5}=e,X=(0,r._T)(e,["data","categories","index","colors","valueFormatter","startEndOnly","showXAxis","showYAxis","yAxisWidth","intervalType","animationDuration","showAnimation","showTooltip","showLegend","showGridLines","autoMinValue","curveType","minValue","maxValue","connectNulls","allowDecimals","noDataText","className","onValueChange","enableLegendSlider","customTooltip","rotateLabelX","tickGap"]),$=j||P?20:0,[Y,Q]=(0,o.useState)(60),[J,ee]=(0,o.useState)(void 0),[et,en]=(0,o.useState)(void 0),er=(0,b.me)(a,E),eo=(0,b.i4)(D,L,z),ei=!!W;function ea(e){ei&&(e===et&&!J||(0,b.FB)(n,e)&&J&&J.dataKey===e?(en(void 0),null==W||W(null)):(en(e),null==W||W({eventType:"category",categoryClicked:e})),ee(void 0))}return o.createElement("div",Object.assign({ref:t,className:(0,S.q)("w-full h-80",q)},X),o.createElement(i.h,{className:"h-full w-full"},(null==n?void 0:n.length)?o.createElement(d,{data:n,onClick:ei&&(et||J)?()=>{ee(void 0),en(void 0),null==W||W(null)}:void 0},_?o.createElement(f.q,{className:(0,S.q)("stroke-1","stroke-tremor-border","dark:stroke-dark-tremor-border"),horizontal:!0,vertical:!1}):null,o.createElement(c.K,{padding:{left:$,right:$},hide:!j,dataKey:u,interval:O?"preserveStartEnd":M,tick:{transform:"translate(0, 6)"},ticks:O?[n[0][u],n[n.length-1][u]]:void 0,fill:"",stroke:"",className:(0,S.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickLine:!1,axisLine:!1,minTickGap:G,angle:null==U?void 0:U.angle,dy:null==U?void 0:U.verticalShift,height:null==U?void 0:U.xAxisHeight}),o.createElement(s.B,{width:N,hide:!P,axisLine:!1,tickLine:!1,type:"number",domain:eo,tick:{transform:"translate(-3, 0)"},fill:"",stroke:"",className:(0,S.q)("text-tremor-label","fill-tremor-content","dark:fill-dark-tremor-content"),tickFormatter:C,allowDecimals:F}),o.createElement(p.u,{wrapperStyle:{outline:"none"},isAnimationActive:!1,cursor:{stroke:"#d1d5db",strokeWidth:1},content:T?e=>{let{active:t,payload:n,label:r}=e;return V?o.createElement(V,{payload:null==n?void 0:n.map(e=>{var t;return Object.assign(Object.assign({},e),{color:null!==(t=er.get(e.dataKey))&&void 0!==t?t:x.fr.Gray})}),active:t,label:r}):o.createElement(v.ZP,{active:t,payload:n,label:r,valueFormatter:C,categoryColors:er})}:o.createElement(o.Fragment,null),position:{y:0}}),A?o.createElement(h.D,{verticalAlign:"top",height:Y,content:e=>{let{payload:t}=e;return(0,g.Z)({payload:t},er,Q,et,ei?e=>ea(e):void 0,K)}}):null,a.map(e=>{var t;return o.createElement(l.x,{className:(0,S.q)((0,k.bM)(null!==(t=er.get(e))&&void 0!==t?t:x.fr.Gray,w.K.text).strokeColor),strokeOpacity:J||et&&et!==e?.3:1,activeDot:e=>{var t;let{cx:r,cy:i,stroke:a,strokeLinecap:l,strokeLinejoin:c,strokeWidth:s,dataKey:u}=e;return o.createElement(m.o,{className:(0,S.q)("stroke-tremor-background dark:stroke-dark-tremor-background",W?"cursor-pointer":"",(0,k.bM)(null!==(t=er.get(u))&&void 0!==t?t:x.fr.Gray,w.K.text).fillColor),cx:r,cy:i,r:5,fill:"",stroke:a,strokeLinecap:l,strokeLinejoin:c,strokeWidth:s,onClick:(t,r)=>{r.stopPropagation(),ei&&(e.index===(null==J?void 0:J.index)&&e.dataKey===(null==J?void 0:J.dataKey)||(0,b.FB)(n,e.dataKey)&&et&&et===e.dataKey?(en(void 0),ee(void 0),null==W||W(null)):(en(e.dataKey),ee({index:e.index,dataKey:e.dataKey}),null==W||W(Object.assign({eventType:"dot",categoryClicked:e.dataKey},e.payload))))}})},dot:t=>{var r;let{stroke:i,strokeLinecap:a,strokeLinejoin:l,strokeWidth:c,cx:s,cy:u,dataKey:d,index:f}=t;return(0,b.FB)(n,e)&&!(J||et&&et!==e)||(null==J?void 0:J.index)===f&&(null==J?void 0:J.dataKey)===e?o.createElement(m.o,{key:f,cx:s,cy:u,r:5,stroke:i,fill:"",strokeLinecap:a,strokeLinejoin:l,strokeWidth:c,className:(0,S.q)("stroke-tremor-background dark:stroke-dark-tremor-background",W?"cursor-pointer":"",(0,k.bM)(null!==(r=er.get(d))&&void 0!==r?r:x.fr.Gray,w.K.text).fillColor)}):o.createElement(o.Fragment,{key:f})},key:e,name:e,type:Z,dataKey:e,stroke:"",strokeWidth:2,strokeLinejoin:"round",strokeLinecap:"round",isAnimationActive:R,animationDuration:I,connectNulls:B})}),W?a.map(e=>o.createElement(l.x,{className:(0,S.q)("cursor-pointer"),strokeOpacity:0,key:e,name:e,type:Z,dataKey:e,stroke:"transparent",fill:"transparent",legendType:"none",tooltipType:"none",strokeWidth:12,connectNulls:B,onClick:(e,t)=>{t.stopPropagation();let{name:n}=e;ea(n)}})):null):o.createElement(y.Z,{noDataText:H})))});E.displayName="LineChart"},65278:function(e,t,n){"use strict";n.d(t,{Z:function(){return m}});var r=n(2265);let o=(e,t)=>{let[n,o]=(0,r.useState)(t);(0,r.useEffect)(()=>{let t=()=>{o(window.innerWidth),e()};return t(),window.addEventListener("resize",t),()=>window.removeEventListener("resize",t)},[e,n])};var i=n(5853),a=n(26898),l=n(65954),c=n(1153);let s=e=>{var t=(0,i._T)(e,[]);return r.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),r.createElement("path",{d:"M8 12L14 6V18L8 12Z"}))},u=e=>{var t=(0,i._T)(e,[]);return r.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"}),r.createElement("path",{d:"M16 12L10 18V6L16 12Z"}))},d=(0,c.fn)("Legend"),f=e=>{let{name:t,color:n,onClick:o,activeLegend:i}=e,s=!!o;return r.createElement("li",{className:(0,l.q)(d("legendItem"),"group inline-flex items-center px-2 py-0.5 rounded-tremor-small transition whitespace-nowrap",s?"cursor-pointer":"cursor-default","text-tremor-content",s?"hover:bg-tremor-background-subtle":"","dark:text-dark-tremor-content",s?"dark:hover:bg-dark-tremor-background-subtle":""),onClick:e=>{e.stopPropagation(),null==o||o(t,n)}},r.createElement("svg",{className:(0,l.q)("flex-none h-2 w-2 mr-1.5",(0,c.bM)(n,a.K.text).textColor,i&&i!==t?"opacity-40":"opacity-100"),fill:"currentColor",viewBox:"0 0 8 8"},r.createElement("circle",{cx:4,cy:4,r:4})),r.createElement("p",{className:(0,l.q)("whitespace-nowrap truncate text-tremor-default","text-tremor-content",s?"group-hover:text-tremor-content-emphasis":"","dark:text-dark-tremor-content",i&&i!==t?"opacity-40":"opacity-100",s?"dark:group-hover:text-dark-tremor-content-emphasis":"")},t))},p=e=>{let{icon:t,onClick:n,disabled:o}=e,[i,a]=r.useState(!1),c=r.useRef(null);return r.useEffect(()=>(i?c.current=setInterval(()=>{null==n||n()},300):clearInterval(c.current),()=>clearInterval(c.current)),[i,n]),(0,r.useEffect)(()=>{o&&(clearInterval(c.current),a(!1))},[o]),r.createElement("button",{type:"button",className:(0,l.q)(d("legendSliderButton"),"w-5 group inline-flex items-center truncate rounded-tremor-small transition",o?"cursor-not-allowed":"cursor-pointer",o?"text-tremor-content-subtle":"text-tremor-content hover:text-tremor-content-emphasis hover:bg-tremor-background-subtle",o?"dark:text-dark-tremor-subtle":"dark:text-dark-tremor dark:hover:text-tremor-content-emphasis dark:hover:bg-dark-tremor-background-subtle"),disabled:o,onClick:e=>{e.stopPropagation(),null==n||n()},onMouseDown:e=>{e.stopPropagation(),a(!0)},onMouseUp:e=>{e.stopPropagation(),a(!1)}},r.createElement(t,{className:"w-full"}))},h=r.forwardRef((e,t)=>{var n,o;let{categories:c,colors:h=a.s,className:m,onClickLegendItem:g,activeLegend:v,enableLegendSlider:y=!1}=e,b=(0,i._T)(e,["categories","colors","className","onClickLegendItem","activeLegend","enableLegendSlider"]),x=r.useRef(null),[w,S]=r.useState(null),[k,E]=r.useState(null),C=r.useRef(null),O=(0,r.useCallback)(()=>{let e=null==x?void 0:x.current;e&&S({left:e.scrollLeft>0,right:e.scrollWidth-e.clientWidth>e.scrollLeft})},[S]),j=(0,r.useCallback)(e=>{var t;let n=null==x?void 0:x.current,r=null!==(t=null==n?void 0:n.clientWidth)&&void 0!==t?t:0;n&&y&&(n.scrollTo({left:"left"===e?n.scrollLeft-r:n.scrollLeft+r,behavior:"smooth"}),setTimeout(()=>{O()},400))},[y,O]);r.useEffect(()=>{let e=e=>{"ArrowLeft"===e?j("left"):"ArrowRight"===e&&j("right")};return k?(e(k),C.current=setInterval(()=>{e(k)},300)):clearInterval(C.current),()=>clearInterval(C.current)},[k,j]);let P=e=>{e.stopPropagation(),"ArrowLeft"!==e.key&&"ArrowRight"!==e.key||(e.preventDefault(),E(e.key))},N=e=>{e.stopPropagation(),E(null)};return r.useEffect(()=>{let e=null==x?void 0:x.current;return y&&(O(),null==e||e.addEventListener("keydown",P),null==e||e.addEventListener("keyup",N)),()=>{null==e||e.removeEventListener("keydown",P),null==e||e.removeEventListener("keyup",N)}},[O,y]),r.createElement("ol",Object.assign({ref:t,className:(0,l.q)(d("root"),"relative overflow-hidden",m)},b),r.createElement("div",{ref:x,tabIndex:0,className:(0,l.q)("h-full flex",y?(null==w?void 0:w.right)||(null==w?void 0:w.left)?"pl-4 pr-12 items-center overflow-auto snap-mandatory [&::-webkit-scrollbar]:hidden [scrollbar-width:none]":"":"flex-wrap")},c.map((e,t)=>r.createElement(f,{key:"item-".concat(t),name:e,color:h[t],onClick:g,activeLegend:v}))),y&&((null==w?void 0:w.right)||(null==w?void 0:w.left))?r.createElement(r.Fragment,null,r.createElement("div",{className:(0,l.q)("from-tremor-background","dark:from-dark-tremor-background","absolute top-0 bottom-0 left-0 w-4 bg-gradient-to-r to-transparent pointer-events-none")}),r.createElement("div",{className:(0,l.q)("to-tremor-background","dark:to-dark-tremor-background","absolute top-0 bottom-0 right-10 w-4 bg-gradient-to-r from-transparent pointer-events-none")}),r.createElement("div",{className:(0,l.q)("bg-tremor-background","dark:bg-dark-tremor-background","absolute flex top-0 pr-1 bottom-0 right-0 items-center justify-center h-full")},r.createElement(p,{icon:s,onClick:()=>{E(null),j("left")},disabled:!(null==w?void 0:w.left)}),r.createElement(p,{icon:u,onClick:()=>{E(null),j("right")},disabled:!(null==w?void 0:w.right)}))):null)});h.displayName="Legend";let m=(e,t,n,i,a,l)=>{let{payload:c}=e,s=(0,r.useRef)(null);o(()=>{var e,t;n((t=null===(e=s.current)||void 0===e?void 0:e.clientHeight)?Number(t)+20:60)});let u=c.filter(e=>"none"!==e.type);return r.createElement("div",{ref:s,className:"flex items-center justify-end"},r.createElement(h,{categories:u.map(e=>e.value),colors:u.map(e=>t.get(e.value)),onClickLegendItem:a,activeLegend:i,enableLegendSlider:l}))}},98593:function(e,t,n){"use strict";n.d(t,{$B:function(){return c},ZP:function(){return u},zX:function(){return s}});var r=n(2265),o=n(7084),i=n(26898),a=n(65954),l=n(1153);let c=e=>{let{children:t}=e;return r.createElement("div",{className:(0,a.q)("rounded-tremor-default text-tremor-default border","bg-tremor-background shadow-tremor-dropdown border-tremor-border","dark:bg-dark-tremor-background dark:shadow-dark-tremor-dropdown dark:border-dark-tremor-border")},t)},s=e=>{let{value:t,name:n,color:o}=e;return r.createElement("div",{className:"flex items-center justify-between space-x-8"},r.createElement("div",{className:"flex items-center space-x-2"},r.createElement("span",{className:(0,a.q)("shrink-0 rounded-tremor-full border-2 h-3 w-3","border-tremor-background shadow-tremor-card","dark:border-dark-tremor-background dark:shadow-dark-tremor-card",(0,l.bM)(o,i.K.background).bgColor)}),r.createElement("p",{className:(0,a.q)("text-right whitespace-nowrap","text-tremor-content","dark:text-dark-tremor-content")},n)),r.createElement("p",{className:(0,a.q)("font-medium tabular-nums text-right whitespace-nowrap","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis")},t))},u=e=>{let{active:t,payload:n,label:i,categoryColors:l,valueFormatter:u}=e;if(t&&n){let e=n.filter(e=>"none"!==e.type);return r.createElement(c,null,r.createElement("div",{className:(0,a.q)("border-tremor-border border-b px-4 py-2","dark:border-dark-tremor-border")},r.createElement("p",{className:(0,a.q)("font-medium","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis")},i)),r.createElement("div",{className:(0,a.q)("px-4 py-2 space-y-1")},e.map((e,t)=>{var n;let{value:i,name:a}=e;return r.createElement(s,{key:"id-".concat(t),value:u(i),name:a,color:null!==(n=l.get(a))&&void 0!==n?n:o.fr.Blue})})))}return null}},69448:function(e,t,n){"use strict";n.d(t,{Z:function(){return f}});var r=n(65954),o=n(2265),i=n(5853);let a=(0,n(1153).fn)("Flex"),l={start:"justify-start",end:"justify-end",center:"justify-center",between:"justify-between",around:"justify-around",evenly:"justify-evenly"},c={start:"items-start",end:"items-end",center:"items-center",baseline:"items-baseline",stretch:"items-stretch"},s={row:"flex-row",col:"flex-col","row-reverse":"flex-row-reverse","col-reverse":"flex-col-reverse"},u=o.forwardRef((e,t)=>{let{flexDirection:n="row",justifyContent:u="between",alignItems:d="center",children:f,className:p}=e,h=(0,i._T)(e,["flexDirection","justifyContent","alignItems","children","className"]);return o.createElement("div",Object.assign({ref:t,className:(0,r.q)(a("root"),"flex w-full",s[n],l[u],c[d],p)},h),f)});u.displayName="Flex";var d=n(84264);let f=e=>{let{noDataText:t="No data"}=e;return o.createElement(u,{alignItems:"center",justifyContent:"center",className:(0,r.q)("w-full h-full border border-dashed rounded-tremor-default","border-tremor-border","dark:border-dark-tremor-border")},o.createElement(d.Z,{className:(0,r.q)("text-tremor-content","dark:text-dark-tremor-content")},t))}},32644:function(e,t,n){"use strict";n.d(t,{FB:function(){return i},i4:function(){return o},me:function(){return r},vZ:function(){return function e(t,n){if(t===n)return!0;if("object"!=typeof t||"object"!=typeof n||null===t||null===n)return!1;let r=Object.keys(t),o=Object.keys(n);if(r.length!==o.length)return!1;for(let i of r)if(!o.includes(i)||!e(t[i],n[i]))return!1;return!0}}});let r=(e,t)=>{let n=new Map;return e.forEach((e,r)=>{n.set(e,t[r])}),n},o=(e,t,n)=>[e?"auto":null!=t?t:0,null!=n?n:"auto"];function i(e,t){let n=[];for(let r of e)if(Object.prototype.hasOwnProperty.call(r,t)&&(n.push(r[t]),n.length>1))return!1;return!0}},41649:function(e,t,n){"use strict";n.d(t,{Z:function(){return p}});var r=n(5853),o=n(2265),i=n(1526),a=n(7084),l=n(26898),c=n(65954),s=n(1153);let u={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},d={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},f=(0,s.fn)("Badge"),p=o.forwardRef((e,t)=>{let{color:n,icon:p,size:h=a.u8.SM,tooltip:m,className:g,children:v}=e,y=(0,r._T)(e,["color","icon","size","tooltip","className","children"]),b=p||null,{tooltipProps:x,getReferenceProps:w}=(0,i.l)();return o.createElement("span",Object.assign({ref:(0,s.lq)([t,x.refs.setReference]),className:(0,c.q)(f("root"),"w-max flex-shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-full",n?(0,c.q)((0,s.bM)(n,l.K.background).bgColor,(0,s.bM)(n,l.K.text).textColor,"bg-opacity-20 dark:bg-opacity-25"):(0,c.q)("bg-tremor-brand-muted text-tremor-brand-emphasis","dark:bg-dark-tremor-brand-muted dark:text-dark-tremor-brand-emphasis"),u[h].paddingX,u[h].paddingY,u[h].fontSize,g)},w,y),o.createElement(i.Z,Object.assign({text:m},x)),b?o.createElement(b,{className:(0,c.q)(f("icon"),"shrink-0 -ml-1 mr-1.5",d[h].height,d[h].width)}):null,o.createElement("p",{className:(0,c.q)(f("text"),"text-sm whitespace-nowrap")},v))});p.displayName="Badge"},47323:function(e,t,n){"use strict";n.d(t,{Z:function(){return m}});var r=n(5853),o=n(2265),i=n(1526),a=n(7084),l=n(65954),c=n(1153),s=n(26898);let u={xs:{paddingX:"px-1.5",paddingY:"py-1.5"},sm:{paddingX:"px-1.5",paddingY:"py-1.5"},md:{paddingX:"px-2",paddingY:"py-2"},lg:{paddingX:"px-2",paddingY:"py-2"},xl:{paddingX:"px-2.5",paddingY:"py-2.5"}},d={xs:{height:"h-3",width:"w-3"},sm:{height:"h-5",width:"w-5"},md:{height:"h-5",width:"w-5"},lg:{height:"h-7",width:"w-7"},xl:{height:"h-9",width:"w-9"}},f={simple:{rounded:"",border:"",ring:"",shadow:""},light:{rounded:"rounded-tremor-default",border:"",ring:"",shadow:""},shadow:{rounded:"rounded-tremor-default",border:"border",ring:"",shadow:"shadow-tremor-card dark:shadow-dark-tremor-card"},solid:{rounded:"rounded-tremor-default",border:"border-2",ring:"ring-1",shadow:""},outlined:{rounded:"rounded-tremor-default",border:"border",ring:"ring-2",shadow:""}},p=(e,t)=>{switch(e){case"simple":return{textColor:t?(0,c.bM)(t,s.K.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:"",borderColor:"",ringColor:""};case"light":return{textColor:t?(0,c.bM)(t,s.K.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.q)((0,c.bM)(t,s.K.background).bgColor,"bg-opacity-20"):"bg-tremor-brand-muted dark:bg-dark-tremor-brand-muted",borderColor:"",ringColor:""};case"shadow":return{textColor:t?(0,c.bM)(t,s.K.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.q)((0,c.bM)(t,s.K.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:"border-tremor-border dark:border-dark-tremor-border",ringColor:""};case"solid":return{textColor:t?(0,c.bM)(t,s.K.text).textColor:"text-tremor-brand-inverted dark:text-dark-tremor-brand-inverted",bgColor:t?(0,l.q)((0,c.bM)(t,s.K.background).bgColor,"bg-opacity-20"):"bg-tremor-brand dark:bg-dark-tremor-brand",borderColor:"border-tremor-brand-inverted dark:border-dark-tremor-brand-inverted",ringColor:"ring-tremor-ring dark:ring-dark-tremor-ring"};case"outlined":return{textColor:t?(0,c.bM)(t,s.K.text).textColor:"text-tremor-brand dark:text-dark-tremor-brand",bgColor:t?(0,l.q)((0,c.bM)(t,s.K.background).bgColor,"bg-opacity-20"):"bg-tremor-background dark:bg-dark-tremor-background",borderColor:t?(0,c.bM)(t,s.K.ring).borderColor:"border-tremor-brand-subtle dark:border-dark-tremor-brand-subtle",ringColor:t?(0,l.q)((0,c.bM)(t,s.K.ring).ringColor,"ring-opacity-40"):"ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted"}}},h=(0,c.fn)("Icon"),m=o.forwardRef((e,t)=>{let{icon:n,variant:s="simple",tooltip:m,size:g=a.u8.SM,color:v,className:y}=e,b=(0,r._T)(e,["icon","variant","tooltip","size","color","className"]),x=p(s,v),{tooltipProps:w,getReferenceProps:S}=(0,i.l)();return o.createElement("span",Object.assign({ref:(0,c.lq)([t,w.refs.setReference]),className:(0,l.q)(h("root"),"inline-flex flex-shrink-0 items-center",x.bgColor,x.textColor,x.borderColor,x.ringColor,f[s].rounded,f[s].border,f[s].shadow,f[s].ring,u[g].paddingX,u[g].paddingY,y)},S,b),o.createElement(i.Z,Object.assign({text:m},w)),o.createElement(n,{className:(0,l.q)(h("icon"),"shrink-0",d[g].height,d[g].width)}))});m.displayName="Icon"},53003:function(e,t,n){"use strict";let r,o,i;n.d(t,{Z:function(){return nF}});var a,l,c,s,u=n(5853),d=n(2265),f=n(54887),p=n(13323),h=n(64518),m=n(96822),g=n(40293);function v(){for(var e=arguments.length,t=Array(e),n=0;n(0,g.r)(...t),[...t])}var y=n(72238),b=n(93689);let x=(0,d.createContext)(!1);var w=n(61424),S=n(27847);let k=d.Fragment,E=d.Fragment,C=(0,d.createContext)(null),O=(0,d.createContext)(null);Object.assign((0,S.yV)(function(e,t){var n;let r,o,i=(0,d.useRef)(null),a=(0,b.T)((0,b.h)(e=>{i.current=e}),t),l=v(i),c=function(e){let t=(0,d.useContext)(x),n=(0,d.useContext)(C),r=v(e),[o,i]=(0,d.useState)(()=>{if(!t&&null!==n||w.O.isServer)return null;let e=null==r?void 0:r.getElementById("headlessui-portal-root");if(e)return e;if(null===r)return null;let o=r.createElement("div");return o.setAttribute("id","headlessui-portal-root"),r.body.appendChild(o)});return(0,d.useEffect)(()=>{null!==o&&(null!=r&&r.body.contains(o)||null==r||r.body.appendChild(o))},[o,r]),(0,d.useEffect)(()=>{t||null!==n&&i(n.current)},[n,i,t]),o}(i),[s]=(0,d.useState)(()=>{var e;return w.O.isServer?null:null!=(e=null==l?void 0:l.createElement("div"))?e:null}),u=(0,d.useContext)(O),g=(0,y.H)();return(0,h.e)(()=>{!c||!s||c.contains(s)||(s.setAttribute("data-headlessui-portal",""),c.appendChild(s))},[c,s]),(0,h.e)(()=>{if(s&&u)return u.register(s)},[u,s]),n=()=>{var e;c&&s&&(s instanceof Node&&c.contains(s)&&c.removeChild(s),c.childNodes.length<=0&&(null==(e=c.parentElement)||e.removeChild(c)))},r=(0,p.z)(n),o=(0,d.useRef)(!1),(0,d.useEffect)(()=>(o.current=!1,()=>{o.current=!0,(0,m.Y)(()=>{o.current&&r()})}),[r]),g&&c&&s?(0,f.createPortal)((0,S.sY)({ourProps:{ref:a},theirProps:e,defaultTag:k,name:"Portal"}),s):null}),{Group:(0,S.yV)(function(e,t){let{target:n,...r}=e,o={ref:(0,b.T)(t)};return d.createElement(C.Provider,{value:n},(0,S.sY)({ourProps:o,theirProps:r,defaultTag:E,name:"Popover.Group"}))})});var j=n(31948),P=n(17684),N=n(98505),M=n(80004),I=n(38198),R=n(3141),T=((r=T||{})[r.Forwards=0]="Forwards",r[r.Backwards=1]="Backwards",r);function A(){let e=(0,d.useRef)(0);return(0,R.s)("keydown",t=>{"Tab"===t.key&&(e.current=t.shiftKey?1:0)},!0),e}var _=n(37863),D=n(47634),Z=n(37105),L=n(24536),z=n(37388),B=((o=B||{})[o.Open=0]="Open",o[o.Closed=1]="Closed",o),F=((i=F||{})[i.TogglePopover=0]="TogglePopover",i[i.ClosePopover=1]="ClosePopover",i[i.SetButton=2]="SetButton",i[i.SetButtonId=3]="SetButtonId",i[i.SetPanel=4]="SetPanel",i[i.SetPanelId=5]="SetPanelId",i);let H={0:e=>{let t={...e,popoverState:(0,L.E)(e.popoverState,{0:1,1:0})};return 0===t.popoverState&&(t.__demoMode=!1),t},1:e=>1===e.popoverState?e:{...e,popoverState:1},2:(e,t)=>e.button===t.button?e:{...e,button:t.button},3:(e,t)=>e.buttonId===t.buttonId?e:{...e,buttonId:t.buttonId},4:(e,t)=>e.panel===t.panel?e:{...e,panel:t.panel},5:(e,t)=>e.panelId===t.panelId?e:{...e,panelId:t.panelId}},q=(0,d.createContext)(null);function W(e){let t=(0,d.useContext)(q);if(null===t){let t=Error("<".concat(e," /> is missing a parent component."));throw Error.captureStackTrace&&Error.captureStackTrace(t,W),t}return t}q.displayName="PopoverContext";let K=(0,d.createContext)(null);function V(e){let t=(0,d.useContext)(K);if(null===t){let t=Error("<".concat(e," /> is missing a parent component."));throw Error.captureStackTrace&&Error.captureStackTrace(t,V),t}return t}K.displayName="PopoverAPIContext";let U=(0,d.createContext)(null);function G(){return(0,d.useContext)(U)}U.displayName="PopoverGroupContext";let X=(0,d.createContext)(null);function $(e,t){return(0,L.E)(t.type,H,e,t)}X.displayName="PopoverPanelContext";let Y=S.AN.RenderStrategy|S.AN.Static,Q=S.AN.RenderStrategy|S.AN.Static,J=Object.assign((0,S.yV)(function(e,t){var n,r,o,i;let a,l,c,s,u,f;let{__demoMode:h=!1,...m}=e,g=(0,d.useRef)(null),y=(0,b.T)(t,(0,b.h)(e=>{g.current=e})),x=(0,d.useRef)([]),w=(0,d.useReducer)($,{__demoMode:h,popoverState:h?0:1,buttons:x,button:null,buttonId:null,panel:null,panelId:null,beforePanelSentinel:(0,d.createRef)(),afterPanelSentinel:(0,d.createRef)()}),[{popoverState:k,button:E,buttonId:C,panel:P,panelId:M,beforePanelSentinel:R,afterPanelSentinel:T},A]=w,D=v(null!=(n=g.current)?n:E),z=(0,d.useMemo)(()=>{if(!E||!P)return!1;for(let e of document.querySelectorAll("body > *"))if(Number(null==e?void 0:e.contains(E))^Number(null==e?void 0:e.contains(P)))return!0;let e=(0,Z.GO)(),t=e.indexOf(E),n=(t+e.length-1)%e.length,r=(t+1)%e.length,o=e[n],i=e[r];return!P.contains(o)&&!P.contains(i)},[E,P]),B=(0,j.E)(C),F=(0,j.E)(M),H=(0,d.useMemo)(()=>({buttonId:B,panelId:F,close:()=>A({type:1})}),[B,F,A]),W=G(),V=null==W?void 0:W.registerPopover,U=(0,p.z)(()=>{var e;return null!=(e=null==W?void 0:W.isFocusWithinPopoverGroup())?e:(null==D?void 0:D.activeElement)&&((null==E?void 0:E.contains(D.activeElement))||(null==P?void 0:P.contains(D.activeElement)))});(0,d.useEffect)(()=>null==V?void 0:V(H),[V,H]);let[Y,Q]=(a=(0,d.useContext)(O),l=(0,d.useRef)([]),c=(0,p.z)(e=>(l.current.push(e),a&&a.register(e),()=>s(e))),s=(0,p.z)(e=>{let t=l.current.indexOf(e);-1!==t&&l.current.splice(t,1),a&&a.unregister(e)}),u=(0,d.useMemo)(()=>({register:c,unregister:s,portals:l}),[c,s,l]),[l,(0,d.useMemo)(()=>function(e){let{children:t}=e;return d.createElement(O.Provider,{value:u},t)},[u])]),J=function(){var e;let{defaultContainers:t=[],portals:n,mainTreeNodeRef:r}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},o=(0,d.useRef)(null!=(e=null==r?void 0:r.current)?e:null),i=v(o),a=(0,p.z)(()=>{var e,r,a;let l=[];for(let e of t)null!==e&&(e instanceof HTMLElement?l.push(e):"current"in e&&e.current instanceof HTMLElement&&l.push(e.current));if(null!=n&&n.current)for(let e of n.current)l.push(e);for(let t of null!=(e=null==i?void 0:i.querySelectorAll("html > *, body > *"))?e:[])t!==document.body&&t!==document.head&&t instanceof HTMLElement&&"headlessui-portal-root"!==t.id&&(t.contains(o.current)||t.contains(null==(a=null==(r=o.current)?void 0:r.getRootNode())?void 0:a.host)||l.some(e=>t.contains(e))||l.push(t));return l});return{resolveContainers:a,contains:(0,p.z)(e=>a().some(t=>t.contains(e))),mainTreeNodeRef:o,MainTreeNode:(0,d.useMemo)(()=>function(){return null!=r?null:d.createElement(I._,{features:I.A.Hidden,ref:o})},[o,r])}}({mainTreeNodeRef:null==W?void 0:W.mainTreeNodeRef,portals:Y,defaultContainers:[E,P]});r=null==D?void 0:D.defaultView,o="focus",i=e=>{var t,n,r,o;e.target!==window&&e.target instanceof HTMLElement&&0===k&&(U()||E&&P&&(J.contains(e.target)||null!=(n=null==(t=R.current)?void 0:t.contains)&&n.call(t,e.target)||null!=(o=null==(r=T.current)?void 0:r.contains)&&o.call(r,e.target)||A({type:1})))},f=(0,j.E)(i),(0,d.useEffect)(()=>{function e(e){f.current(e)}return(r=null!=r?r:window).addEventListener(o,e,!0),()=>r.removeEventListener(o,e,!0)},[r,o,!0]),(0,N.O)(J.resolveContainers,(e,t)=>{A({type:1}),(0,Z.sP)(t,Z.tJ.Loose)||(e.preventDefault(),null==E||E.focus())},0===k);let ee=(0,p.z)(e=>{A({type:1});let t=e?e instanceof HTMLElement?e:"current"in e&&e.current instanceof HTMLElement?e.current:E:E;null==t||t.focus()}),et=(0,d.useMemo)(()=>({close:ee,isPortalled:z}),[ee,z]),en=(0,d.useMemo)(()=>({open:0===k,close:ee}),[k,ee]);return d.createElement(X.Provider,{value:null},d.createElement(q.Provider,{value:w},d.createElement(K.Provider,{value:et},d.createElement(_.up,{value:(0,L.E)(k,{0:_.ZM.Open,1:_.ZM.Closed})},d.createElement(Q,null,(0,S.sY)({ourProps:{ref:y},theirProps:m,slot:en,defaultTag:"div",name:"Popover"}),d.createElement(J.MainTreeNode,null))))))}),{Button:(0,S.yV)(function(e,t){let n=(0,P.M)(),{id:r="headlessui-popover-button-".concat(n),...o}=e,[i,a]=W("Popover.Button"),{isPortalled:l}=V("Popover.Button"),c=(0,d.useRef)(null),s="headlessui-focus-sentinel-".concat((0,P.M)()),u=G(),f=null==u?void 0:u.closeOthers,h=null!==(0,d.useContext)(X);(0,d.useEffect)(()=>{if(!h)return a({type:3,buttonId:r}),()=>{a({type:3,buttonId:null})}},[h,r,a]);let[m]=(0,d.useState)(()=>Symbol()),g=(0,b.T)(c,t,h?null:e=>{if(e)i.buttons.current.push(m);else{let e=i.buttons.current.indexOf(m);-1!==e&&i.buttons.current.splice(e,1)}i.buttons.current.length>1&&console.warn("You are already using a but only 1 is supported."),e&&a({type:2,button:e})}),y=(0,b.T)(c,t),x=v(c),w=(0,p.z)(e=>{var t,n,r;if(h){if(1===i.popoverState)return;switch(e.key){case z.R.Space:case z.R.Enter:e.preventDefault(),null==(n=(t=e.target).click)||n.call(t),a({type:1}),null==(r=i.button)||r.focus()}}else switch(e.key){case z.R.Space:case z.R.Enter:e.preventDefault(),e.stopPropagation(),1===i.popoverState&&(null==f||f(i.buttonId)),a({type:0});break;case z.R.Escape:if(0!==i.popoverState)return null==f?void 0:f(i.buttonId);if(!c.current||null!=x&&x.activeElement&&!c.current.contains(x.activeElement))return;e.preventDefault(),e.stopPropagation(),a({type:1})}}),k=(0,p.z)(e=>{h||e.key===z.R.Space&&e.preventDefault()}),E=(0,p.z)(t=>{var n,r;(0,D.P)(t.currentTarget)||e.disabled||(h?(a({type:1}),null==(n=i.button)||n.focus()):(t.preventDefault(),t.stopPropagation(),1===i.popoverState&&(null==f||f(i.buttonId)),a({type:0}),null==(r=i.button)||r.focus()))}),C=(0,p.z)(e=>{e.preventDefault(),e.stopPropagation()}),O=0===i.popoverState,j=(0,d.useMemo)(()=>({open:O}),[O]),N=(0,M.f)(e,c),R=h?{ref:y,type:N,onKeyDown:w,onClick:E}:{ref:g,id:i.buttonId,type:N,"aria-expanded":0===i.popoverState,"aria-controls":i.panel?i.panelId:void 0,onKeyDown:w,onKeyUp:k,onClick:E,onMouseDown:C},_=A(),B=(0,p.z)(()=>{let e=i.panel;e&&(0,L.E)(_.current,{[T.Forwards]:()=>(0,Z.jA)(e,Z.TO.First),[T.Backwards]:()=>(0,Z.jA)(e,Z.TO.Last)})===Z.fE.Error&&(0,Z.jA)((0,Z.GO)().filter(e=>"true"!==e.dataset.headlessuiFocusGuard),(0,L.E)(_.current,{[T.Forwards]:Z.TO.Next,[T.Backwards]:Z.TO.Previous}),{relativeTo:i.button})});return d.createElement(d.Fragment,null,(0,S.sY)({ourProps:R,theirProps:o,slot:j,defaultTag:"button",name:"Popover.Button"}),O&&!h&&l&&d.createElement(I._,{id:s,features:I.A.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:B}))}),Overlay:(0,S.yV)(function(e,t){let n=(0,P.M)(),{id:r="headlessui-popover-overlay-".concat(n),...o}=e,[{popoverState:i},a]=W("Popover.Overlay"),l=(0,b.T)(t),c=(0,_.oJ)(),s=null!==c?(c&_.ZM.Open)===_.ZM.Open:0===i,u=(0,p.z)(e=>{if((0,D.P)(e.currentTarget))return e.preventDefault();a({type:1})}),f=(0,d.useMemo)(()=>({open:0===i}),[i]);return(0,S.sY)({ourProps:{ref:l,id:r,"aria-hidden":!0,onClick:u},theirProps:o,slot:f,defaultTag:"div",features:Y,visible:s,name:"Popover.Overlay"})}),Panel:(0,S.yV)(function(e,t){let n=(0,P.M)(),{id:r="headlessui-popover-panel-".concat(n),focus:o=!1,...i}=e,[a,l]=W("Popover.Panel"),{close:c,isPortalled:s}=V("Popover.Panel"),u="headlessui-focus-sentinel-before-".concat((0,P.M)()),f="headlessui-focus-sentinel-after-".concat((0,P.M)()),m=(0,d.useRef)(null),g=(0,b.T)(m,t,e=>{l({type:4,panel:e})}),y=v(m),x=(0,S.Y2)();(0,h.e)(()=>(l({type:5,panelId:r}),()=>{l({type:5,panelId:null})}),[r,l]);let w=(0,_.oJ)(),k=null!==w?(w&_.ZM.Open)===_.ZM.Open:0===a.popoverState,E=(0,p.z)(e=>{var t;if(e.key===z.R.Escape){if(0!==a.popoverState||!m.current||null!=y&&y.activeElement&&!m.current.contains(y.activeElement))return;e.preventDefault(),e.stopPropagation(),l({type:1}),null==(t=a.button)||t.focus()}});(0,d.useEffect)(()=>{var t;e.static||1===a.popoverState&&(null==(t=e.unmount)||t)&&l({type:4,panel:null})},[a.popoverState,e.unmount,e.static,l]),(0,d.useEffect)(()=>{if(a.__demoMode||!o||0!==a.popoverState||!m.current)return;let e=null==y?void 0:y.activeElement;m.current.contains(e)||(0,Z.jA)(m.current,Z.TO.First)},[a.__demoMode,o,m,a.popoverState]);let C=(0,d.useMemo)(()=>({open:0===a.popoverState,close:c}),[a,c]),O={ref:g,id:r,onKeyDown:E,onBlur:o&&0===a.popoverState?e=>{var t,n,r,o,i;let c=e.relatedTarget;c&&m.current&&(null!=(t=m.current)&&t.contains(c)||(l({type:1}),(null!=(r=null==(n=a.beforePanelSentinel.current)?void 0:n.contains)&&r.call(n,c)||null!=(i=null==(o=a.afterPanelSentinel.current)?void 0:o.contains)&&i.call(o,c))&&c.focus({preventScroll:!0})))}:void 0,tabIndex:-1},j=A(),N=(0,p.z)(()=>{let e=m.current;e&&(0,L.E)(j.current,{[T.Forwards]:()=>{var t;(0,Z.jA)(e,Z.TO.First)===Z.fE.Error&&(null==(t=a.afterPanelSentinel.current)||t.focus())},[T.Backwards]:()=>{var e;null==(e=a.button)||e.focus({preventScroll:!0})}})}),M=(0,p.z)(()=>{let e=m.current;e&&(0,L.E)(j.current,{[T.Forwards]:()=>{var e;if(!a.button)return;let t=(0,Z.GO)(),n=t.indexOf(a.button),r=t.slice(0,n+1),o=[...t.slice(n+1),...r];for(let t of o.slice())if("true"===t.dataset.headlessuiFocusGuard||null!=(e=a.panel)&&e.contains(t)){let e=o.indexOf(t);-1!==e&&o.splice(e,1)}(0,Z.jA)(o,Z.TO.First,{sorted:!1})},[T.Backwards]:()=>{var t;(0,Z.jA)(e,Z.TO.Previous)===Z.fE.Error&&(null==(t=a.button)||t.focus())}})});return d.createElement(X.Provider,{value:r},k&&s&&d.createElement(I._,{id:u,ref:a.beforePanelSentinel,features:I.A.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:N}),(0,S.sY)({mergeRefs:x,ourProps:O,theirProps:i,slot:C,defaultTag:"div",features:Q,visible:k,name:"Popover.Panel"}),k&&s&&d.createElement(I._,{id:f,ref:a.afterPanelSentinel,features:I.A.Focusable,"data-headlessui-focus-guard":!0,as:"button",type:"button",onFocus:M}))}),Group:(0,S.yV)(function(e,t){let n;let r=(0,d.useRef)(null),o=(0,b.T)(r,t),[i,a]=(0,d.useState)([]),l={mainTreeNodeRef:n=(0,d.useRef)(null),MainTreeNode:(0,d.useMemo)(()=>function(){return d.createElement(I._,{features:I.A.Hidden,ref:n})},[n])},c=(0,p.z)(e=>{a(t=>{let n=t.indexOf(e);if(-1!==n){let e=t.slice();return e.splice(n,1),e}return t})}),s=(0,p.z)(e=>(a(t=>[...t,e]),()=>c(e))),u=(0,p.z)(()=>{var e;let t=(0,g.r)(r);if(!t)return!1;let n=t.activeElement;return!!(null!=(e=r.current)&&e.contains(n))||i.some(e=>{var r,o;return(null==(r=t.getElementById(e.buttonId.current))?void 0:r.contains(n))||(null==(o=t.getElementById(e.panelId.current))?void 0:o.contains(n))})}),f=(0,p.z)(e=>{for(let t of i)t.buttonId.current!==e&&t.close()}),h=(0,d.useMemo)(()=>({registerPopover:s,unregisterPopover:c,isFocusWithinPopoverGroup:u,closeOthers:f,mainTreeNodeRef:l.mainTreeNodeRef}),[s,c,u,f,l.mainTreeNodeRef]),m=(0,d.useMemo)(()=>({}),[]);return d.createElement(U.Provider,{value:h},(0,S.sY)({ourProps:{ref:o},theirProps:e,slot:m,defaultTag:"div",name:"Popover.Group"}),d.createElement(l.MainTreeNode,null))})});var ee=n(33044),et=n(28517);let en=e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",fill:"currentColor"}),d.createElement("path",{fillRule:"evenodd",d:"M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z",clipRule:"evenodd"}))};var er=n(4537),eo=n(99735),ei=n(7656);function ea(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e);return t.setHours(0,0,0,0),t}function el(){return ea(Date.now())}function ec(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e);return t.setDate(1),t.setHours(0,0,0,0),t}var es=n(65954),eu=n(96398),ed=n(41154);function ef(e){var t,n;if((0,ei.Z)(1,arguments),e&&"function"==typeof e.forEach)t=e;else{if("object"!==(0,ed.Z)(e)||null===e)return new Date(NaN);t=Array.prototype.slice.call(e)}return t.forEach(function(e){var t=(0,eo.Z)(e);(void 0===n||nt||isNaN(t.getDate()))&&(n=t)}),n||new Date(NaN)}var eh=n(25721),em=n(47869);function eg(e,t){(0,ei.Z)(2,arguments);var n=(0,em.Z)(t);return(0,eh.Z)(e,-n)}var ev=n(55463);function ey(e,t){if((0,ei.Z)(2,arguments),!t||"object"!==(0,ed.Z)(t))return new Date(NaN);var n=t.years?(0,em.Z)(t.years):0,r=t.months?(0,em.Z)(t.months):0,o=t.weeks?(0,em.Z)(t.weeks):0,i=t.days?(0,em.Z)(t.days):0,a=t.hours?(0,em.Z)(t.hours):0,l=t.minutes?(0,em.Z)(t.minutes):0,c=t.seconds?(0,em.Z)(t.seconds):0;return new Date(eg(function(e,t){(0,ei.Z)(2,arguments);var n=(0,em.Z)(t);return(0,ev.Z)(e,-n)}(e,r+12*n),i+7*o).getTime()-1e3*(c+60*(l+60*a)))}function eb(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=new Date(0);return n.setFullYear(t.getFullYear(),0,1),n.setHours(0,0,0,0),n}function ex(e){return(0,ei.Z)(1,arguments),e instanceof Date||"object"===(0,ed.Z)(e)&&"[object Date]"===Object.prototype.toString.call(e)}function ew(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getUTCDay();return t.setUTCDate(t.getUTCDate()-((n<1?7:0)+n-1)),t.setUTCHours(0,0,0,0),t}function eS(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getUTCFullYear(),r=new Date(0);r.setUTCFullYear(n+1,0,4),r.setUTCHours(0,0,0,0);var o=ew(r),i=new Date(0);i.setUTCFullYear(n,0,4),i.setUTCHours(0,0,0,0);var a=ew(i);return t.getTime()>=o.getTime()?n+1:t.getTime()>=a.getTime()?n:n-1}var ek={};function eE(e,t){(0,ei.Z)(1,arguments);var n,r,o,i,a,l,c,s,u=(0,em.Z)(null!==(n=null!==(r=null!==(o=null!==(i=null==t?void 0:t.weekStartsOn)&&void 0!==i?i:null==t?void 0:null===(a=t.locale)||void 0===a?void 0:null===(l=a.options)||void 0===l?void 0:l.weekStartsOn)&&void 0!==o?o:ek.weekStartsOn)&&void 0!==r?r:null===(c=ek.locale)||void 0===c?void 0:null===(s=c.options)||void 0===s?void 0:s.weekStartsOn)&&void 0!==n?n:0);if(!(u>=0&&u<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");var d=(0,eo.Z)(e),f=d.getUTCDay();return d.setUTCDate(d.getUTCDate()-((f=1&&f<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var p=new Date(0);p.setUTCFullYear(d+1,0,f),p.setUTCHours(0,0,0,0);var h=eE(p,t),m=new Date(0);m.setUTCFullYear(d,0,f),m.setUTCHours(0,0,0,0);var g=eE(m,t);return u.getTime()>=h.getTime()?d+1:u.getTime()>=g.getTime()?d:d-1}function eO(e,t){for(var n=Math.abs(e).toString();n.length0?n:1-n;return eO("yy"===t?r%100:r,t.length)},M:function(e,t){var n=e.getUTCMonth();return"M"===t?String(n+1):eO(n+1,2)},d:function(e,t){return eO(e.getUTCDate(),t.length)},h:function(e,t){return eO(e.getUTCHours()%12||12,t.length)},H:function(e,t){return eO(e.getUTCHours(),t.length)},m:function(e,t){return eO(e.getUTCMinutes(),t.length)},s:function(e,t){return eO(e.getUTCSeconds(),t.length)},S:function(e,t){var n=t.length;return eO(Math.floor(e.getUTCMilliseconds()*Math.pow(10,n-3)),t.length)}},eP={midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"};function eN(e,t){var n=e>0?"-":"+",r=Math.abs(e),o=Math.floor(r/60),i=r%60;return 0===i?n+String(o):n+String(o)+(t||"")+eO(i,2)}function eM(e,t){return e%60==0?(e>0?"-":"+")+eO(Math.abs(e)/60,2):eI(e,t)}function eI(e,t){var n=Math.abs(e);return(e>0?"-":"+")+eO(Math.floor(n/60),2)+(t||"")+eO(n%60,2)}var eR={G:function(e,t,n){var r=e.getUTCFullYear()>0?1:0;switch(t){case"G":case"GG":case"GGG":return n.era(r,{width:"abbreviated"});case"GGGGG":return n.era(r,{width:"narrow"});default:return n.era(r,{width:"wide"})}},y:function(e,t,n){if("yo"===t){var r=e.getUTCFullYear();return n.ordinalNumber(r>0?r:1-r,{unit:"year"})}return ej.y(e,t)},Y:function(e,t,n,r){var o=eC(e,r),i=o>0?o:1-o;return"YY"===t?eO(i%100,2):"Yo"===t?n.ordinalNumber(i,{unit:"year"}):eO(i,t.length)},R:function(e,t){return eO(eS(e),t.length)},u:function(e,t){return eO(e.getUTCFullYear(),t.length)},Q:function(e,t,n){var r=Math.ceil((e.getUTCMonth()+1)/3);switch(t){case"Q":return String(r);case"QQ":return eO(r,2);case"Qo":return n.ordinalNumber(r,{unit:"quarter"});case"QQQ":return n.quarter(r,{width:"abbreviated",context:"formatting"});case"QQQQQ":return n.quarter(r,{width:"narrow",context:"formatting"});default:return n.quarter(r,{width:"wide",context:"formatting"})}},q:function(e,t,n){var r=Math.ceil((e.getUTCMonth()+1)/3);switch(t){case"q":return String(r);case"qq":return eO(r,2);case"qo":return n.ordinalNumber(r,{unit:"quarter"});case"qqq":return n.quarter(r,{width:"abbreviated",context:"standalone"});case"qqqqq":return n.quarter(r,{width:"narrow",context:"standalone"});default:return n.quarter(r,{width:"wide",context:"standalone"})}},M:function(e,t,n){var r=e.getUTCMonth();switch(t){case"M":case"MM":return ej.M(e,t);case"Mo":return n.ordinalNumber(r+1,{unit:"month"});case"MMM":return n.month(r,{width:"abbreviated",context:"formatting"});case"MMMMM":return n.month(r,{width:"narrow",context:"formatting"});default:return n.month(r,{width:"wide",context:"formatting"})}},L:function(e,t,n){var r=e.getUTCMonth();switch(t){case"L":return String(r+1);case"LL":return eO(r+1,2);case"Lo":return n.ordinalNumber(r+1,{unit:"month"});case"LLL":return n.month(r,{width:"abbreviated",context:"standalone"});case"LLLLL":return n.month(r,{width:"narrow",context:"standalone"});default:return n.month(r,{width:"wide",context:"standalone"})}},w:function(e,t,n,r){var o=function(e,t){(0,ei.Z)(1,arguments);var n=(0,eo.Z)(e);return Math.round((eE(n,t).getTime()-(function(e,t){(0,ei.Z)(1,arguments);var n,r,o,i,a,l,c,s,u=(0,em.Z)(null!==(n=null!==(r=null!==(o=null!==(i=null==t?void 0:t.firstWeekContainsDate)&&void 0!==i?i:null==t?void 0:null===(a=t.locale)||void 0===a?void 0:null===(l=a.options)||void 0===l?void 0:l.firstWeekContainsDate)&&void 0!==o?o:ek.firstWeekContainsDate)&&void 0!==r?r:null===(c=ek.locale)||void 0===c?void 0:null===(s=c.options)||void 0===s?void 0:s.firstWeekContainsDate)&&void 0!==n?n:1),d=eC(e,t),f=new Date(0);return f.setUTCFullYear(d,0,u),f.setUTCHours(0,0,0,0),eE(f,t)})(n,t).getTime())/6048e5)+1}(e,r);return"wo"===t?n.ordinalNumber(o,{unit:"week"}):eO(o,t.length)},I:function(e,t,n){var r=function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e);return Math.round((ew(t).getTime()-(function(e){(0,ei.Z)(1,arguments);var t=eS(e),n=new Date(0);return n.setUTCFullYear(t,0,4),n.setUTCHours(0,0,0,0),ew(n)})(t).getTime())/6048e5)+1}(e);return"Io"===t?n.ordinalNumber(r,{unit:"week"}):eO(r,t.length)},d:function(e,t,n){return"do"===t?n.ordinalNumber(e.getUTCDate(),{unit:"date"}):ej.d(e,t)},D:function(e,t,n){var r=function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getTime();return t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0),Math.floor((n-t.getTime())/864e5)+1}(e);return"Do"===t?n.ordinalNumber(r,{unit:"dayOfYear"}):eO(r,t.length)},E:function(e,t,n){var r=e.getUTCDay();switch(t){case"E":case"EE":case"EEE":return n.day(r,{width:"abbreviated",context:"formatting"});case"EEEEE":return n.day(r,{width:"narrow",context:"formatting"});case"EEEEEE":return n.day(r,{width:"short",context:"formatting"});default:return n.day(r,{width:"wide",context:"formatting"})}},e:function(e,t,n,r){var o=e.getUTCDay(),i=(o-r.weekStartsOn+8)%7||7;switch(t){case"e":return String(i);case"ee":return eO(i,2);case"eo":return n.ordinalNumber(i,{unit:"day"});case"eee":return n.day(o,{width:"abbreviated",context:"formatting"});case"eeeee":return n.day(o,{width:"narrow",context:"formatting"});case"eeeeee":return n.day(o,{width:"short",context:"formatting"});default:return n.day(o,{width:"wide",context:"formatting"})}},c:function(e,t,n,r){var o=e.getUTCDay(),i=(o-r.weekStartsOn+8)%7||7;switch(t){case"c":return String(i);case"cc":return eO(i,t.length);case"co":return n.ordinalNumber(i,{unit:"day"});case"ccc":return n.day(o,{width:"abbreviated",context:"standalone"});case"ccccc":return n.day(o,{width:"narrow",context:"standalone"});case"cccccc":return n.day(o,{width:"short",context:"standalone"});default:return n.day(o,{width:"wide",context:"standalone"})}},i:function(e,t,n){var r=e.getUTCDay(),o=0===r?7:r;switch(t){case"i":return String(o);case"ii":return eO(o,t.length);case"io":return n.ordinalNumber(o,{unit:"day"});case"iii":return n.day(r,{width:"abbreviated",context:"formatting"});case"iiiii":return n.day(r,{width:"narrow",context:"formatting"});case"iiiiii":return n.day(r,{width:"short",context:"formatting"});default:return n.day(r,{width:"wide",context:"formatting"})}},a:function(e,t,n){var r=e.getUTCHours()/12>=1?"pm":"am";switch(t){case"a":case"aa":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"aaa":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"}).toLowerCase();case"aaaaa":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},b:function(e,t,n){var r,o=e.getUTCHours();switch(r=12===o?eP.noon:0===o?eP.midnight:o/12>=1?"pm":"am",t){case"b":case"bb":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"bbb":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"}).toLowerCase();case"bbbbb":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},B:function(e,t,n){var r,o=e.getUTCHours();switch(r=o>=17?eP.evening:o>=12?eP.afternoon:o>=4?eP.morning:eP.night,t){case"B":case"BB":case"BBB":return n.dayPeriod(r,{width:"abbreviated",context:"formatting"});case"BBBBB":return n.dayPeriod(r,{width:"narrow",context:"formatting"});default:return n.dayPeriod(r,{width:"wide",context:"formatting"})}},h:function(e,t,n){if("ho"===t){var r=e.getUTCHours()%12;return 0===r&&(r=12),n.ordinalNumber(r,{unit:"hour"})}return ej.h(e,t)},H:function(e,t,n){return"Ho"===t?n.ordinalNumber(e.getUTCHours(),{unit:"hour"}):ej.H(e,t)},K:function(e,t,n){var r=e.getUTCHours()%12;return"Ko"===t?n.ordinalNumber(r,{unit:"hour"}):eO(r,t.length)},k:function(e,t,n){var r=e.getUTCHours();return(0===r&&(r=24),"ko"===t)?n.ordinalNumber(r,{unit:"hour"}):eO(r,t.length)},m:function(e,t,n){return"mo"===t?n.ordinalNumber(e.getUTCMinutes(),{unit:"minute"}):ej.m(e,t)},s:function(e,t,n){return"so"===t?n.ordinalNumber(e.getUTCSeconds(),{unit:"second"}):ej.s(e,t)},S:function(e,t){return ej.S(e,t)},X:function(e,t,n,r){var o=(r._originalDate||e).getTimezoneOffset();if(0===o)return"Z";switch(t){case"X":return eM(o);case"XXXX":case"XX":return eI(o);default:return eI(o,":")}},x:function(e,t,n,r){var o=(r._originalDate||e).getTimezoneOffset();switch(t){case"x":return eM(o);case"xxxx":case"xx":return eI(o);default:return eI(o,":")}},O:function(e,t,n,r){var o=(r._originalDate||e).getTimezoneOffset();switch(t){case"O":case"OO":case"OOO":return"GMT"+eN(o,":");default:return"GMT"+eI(o,":")}},z:function(e,t,n,r){var o=(r._originalDate||e).getTimezoneOffset();switch(t){case"z":case"zz":case"zzz":return"GMT"+eN(o,":");default:return"GMT"+eI(o,":")}},t:function(e,t,n,r){return eO(Math.floor((r._originalDate||e).getTime()/1e3),t.length)},T:function(e,t,n,r){return eO((r._originalDate||e).getTime(),t.length)}},eT=function(e,t){switch(e){case"P":return t.date({width:"short"});case"PP":return t.date({width:"medium"});case"PPP":return t.date({width:"long"});default:return t.date({width:"full"})}},eA=function(e,t){switch(e){case"p":return t.time({width:"short"});case"pp":return t.time({width:"medium"});case"ppp":return t.time({width:"long"});default:return t.time({width:"full"})}},e_={p:eA,P:function(e,t){var n,r=e.match(/(P+)(p+)?/)||[],o=r[1],i=r[2];if(!i)return eT(e,t);switch(o){case"P":n=t.dateTime({width:"short"});break;case"PP":n=t.dateTime({width:"medium"});break;case"PPP":n=t.dateTime({width:"long"});break;default:n=t.dateTime({width:"full"})}return n.replace("{{date}}",eT(o,t)).replace("{{time}}",eA(i,t))}};function eD(e){var t=new Date(Date.UTC(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds()));return t.setUTCFullYear(e.getFullYear()),e.getTime()-t.getTime()}var eZ=["D","DD"],eL=["YY","YYYY"];function ez(e,t,n){if("YYYY"===e)throw RangeError("Use `yyyy` instead of `YYYY` (in `".concat(t,"`) for formatting years to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("YY"===e)throw RangeError("Use `yy` instead of `YY` (in `".concat(t,"`) for formatting years to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("D"===e)throw RangeError("Use `d` instead of `D` (in `".concat(t,"`) for formatting days of the month to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"));if("DD"===e)throw RangeError("Use `dd` instead of `DD` (in `".concat(t,"`) for formatting days of the month to the input `").concat(n,"`; see: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md"))}var eB={lessThanXSeconds:{one:"less than a second",other:"less than {{count}} seconds"},xSeconds:{one:"1 second",other:"{{count}} seconds"},halfAMinute:"half a minute",lessThanXMinutes:{one:"less than a minute",other:"less than {{count}} minutes"},xMinutes:{one:"1 minute",other:"{{count}} minutes"},aboutXHours:{one:"about 1 hour",other:"about {{count}} hours"},xHours:{one:"1 hour",other:"{{count}} hours"},xDays:{one:"1 day",other:"{{count}} days"},aboutXWeeks:{one:"about 1 week",other:"about {{count}} weeks"},xWeeks:{one:"1 week",other:"{{count}} weeks"},aboutXMonths:{one:"about 1 month",other:"about {{count}} months"},xMonths:{one:"1 month",other:"{{count}} months"},aboutXYears:{one:"about 1 year",other:"about {{count}} years"},xYears:{one:"1 year",other:"{{count}} years"},overXYears:{one:"over 1 year",other:"over {{count}} years"},almostXYears:{one:"almost 1 year",other:"almost {{count}} years"}};function eF(e){return function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=t.width?String(t.width):e.defaultWidth;return e.formats[n]||e.formats[e.defaultWidth]}}var eH={date:eF({formats:{full:"EEEE, MMMM do, y",long:"MMMM do, y",medium:"MMM d, y",short:"MM/dd/yyyy"},defaultWidth:"full"}),time:eF({formats:{full:"h:mm:ss a zzzz",long:"h:mm:ss a z",medium:"h:mm:ss a",short:"h:mm a"},defaultWidth:"full"}),dateTime:eF({formats:{full:"{{date}} 'at' {{time}}",long:"{{date}} 'at' {{time}}",medium:"{{date}}, {{time}}",short:"{{date}}, {{time}}"},defaultWidth:"full"})},eq={lastWeek:"'last' eeee 'at' p",yesterday:"'yesterday at' p",today:"'today at' p",tomorrow:"'tomorrow at' p",nextWeek:"eeee 'at' p",other:"P"};function eW(e){return function(t,n){var r;if("formatting"===(null!=n&&n.context?String(n.context):"standalone")&&e.formattingValues){var o=e.defaultFormattingWidth||e.defaultWidth,i=null!=n&&n.width?String(n.width):o;r=e.formattingValues[i]||e.formattingValues[o]}else{var a=e.defaultWidth,l=null!=n&&n.width?String(n.width):e.defaultWidth;r=e.values[l]||e.values[a]}return r[e.argumentCallback?e.argumentCallback(t):t]}}function eK(e){return function(t){var n,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=r.width,i=o&&e.matchPatterns[o]||e.matchPatterns[e.defaultMatchWidth],a=t.match(i);if(!a)return null;var l=a[0],c=o&&e.parsePatterns[o]||e.parsePatterns[e.defaultParseWidth],s=Array.isArray(c)?function(e,t){for(var n=0;n0?"in "+r:r+" ago":r},formatLong:eH,formatRelative:function(e,t,n,r){return eq[e]},localize:{ordinalNumber:function(e,t){var n=Number(e),r=n%100;if(r>20||r<10)switch(r%10){case 1:return n+"st";case 2:return n+"nd";case 3:return n+"rd"}return n+"th"},era:eW({values:{narrow:["B","A"],abbreviated:["BC","AD"],wide:["Before Christ","Anno Domini"]},defaultWidth:"wide"}),quarter:eW({values:{narrow:["1","2","3","4"],abbreviated:["Q1","Q2","Q3","Q4"],wide:["1st quarter","2nd quarter","3rd quarter","4th quarter"]},defaultWidth:"wide",argumentCallback:function(e){return e-1}}),month:eW({values:{narrow:["J","F","M","A","M","J","J","A","S","O","N","D"],abbreviated:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wide:["January","February","March","April","May","June","July","August","September","October","November","December"]},defaultWidth:"wide"}),day:eW({values:{narrow:["S","M","T","W","T","F","S"],short:["Su","Mo","Tu","We","Th","Fr","Sa"],abbreviated:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],wide:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},defaultWidth:"wide"}),dayPeriod:eW({values:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"morning",afternoon:"afternoon",evening:"evening",night:"night"}},defaultWidth:"wide",formattingValues:{narrow:{am:"a",pm:"p",midnight:"mi",noon:"n",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},abbreviated:{am:"AM",pm:"PM",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"},wide:{am:"a.m.",pm:"p.m.",midnight:"midnight",noon:"noon",morning:"in the morning",afternoon:"in the afternoon",evening:"in the evening",night:"at night"}},defaultFormattingWidth:"wide"})},match:{ordinalNumber:(a={matchPattern:/^(\d+)(th|st|nd|rd)?/i,parsePattern:/\d+/i,valueCallback:function(e){return parseInt(e,10)}},function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=e.match(a.matchPattern);if(!n)return null;var r=n[0],o=e.match(a.parsePattern);if(!o)return null;var i=a.valueCallback?a.valueCallback(o[0]):o[0];return{value:i=t.valueCallback?t.valueCallback(i):i,rest:e.slice(r.length)}}),era:eK({matchPatterns:{narrow:/^(b|a)/i,abbreviated:/^(b\.?\s?c\.?|b\.?\s?c\.?\s?e\.?|a\.?\s?d\.?|c\.?\s?e\.?)/i,wide:/^(before christ|before common era|anno domini|common era)/i},defaultMatchWidth:"wide",parsePatterns:{any:[/^b/i,/^(a|c)/i]},defaultParseWidth:"any"}),quarter:eK({matchPatterns:{narrow:/^[1234]/i,abbreviated:/^q[1234]/i,wide:/^[1234](th|st|nd|rd)? quarter/i},defaultMatchWidth:"wide",parsePatterns:{any:[/1/i,/2/i,/3/i,/4/i]},defaultParseWidth:"any",valueCallback:function(e){return e+1}}),month:eK({matchPatterns:{narrow:/^[jfmasond]/i,abbreviated:/^(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i,wide:/^(january|february|march|april|may|june|july|august|september|october|november|december)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^j/i,/^f/i,/^m/i,/^a/i,/^m/i,/^j/i,/^j/i,/^a/i,/^s/i,/^o/i,/^n/i,/^d/i],any:[/^ja/i,/^f/i,/^mar/i,/^ap/i,/^may/i,/^jun/i,/^jul/i,/^au/i,/^s/i,/^o/i,/^n/i,/^d/i]},defaultParseWidth:"any"}),day:eK({matchPatterns:{narrow:/^[smtwf]/i,short:/^(su|mo|tu|we|th|fr|sa)/i,abbreviated:/^(sun|mon|tue|wed|thu|fri|sat)/i,wide:/^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)/i},defaultMatchWidth:"wide",parsePatterns:{narrow:[/^s/i,/^m/i,/^t/i,/^w/i,/^t/i,/^f/i,/^s/i],any:[/^su/i,/^m/i,/^tu/i,/^w/i,/^th/i,/^f/i,/^sa/i]},defaultParseWidth:"any"}),dayPeriod:eK({matchPatterns:{narrow:/^(a|p|mi|n|(in the|at) (morning|afternoon|evening|night))/i,any:/^([ap]\.?\s?m\.?|midnight|noon|(in the|at) (morning|afternoon|evening|night))/i},defaultMatchWidth:"any",parsePatterns:{any:{am:/^a/i,pm:/^p/i,midnight:/^mi/i,noon:/^no/i,morning:/morning/i,afternoon:/afternoon/i,evening:/evening/i,night:/night/i}},defaultParseWidth:"any"})},options:{weekStartsOn:0,firstWeekContainsDate:1}},eU=/[yYQqMLwIdDecihHKkms]o|(\w)\1*|''|'(''|[^'])+('|$)|./g,eG=/P+p+|P+|p+|''|'(''|[^'])+('|$)|./g,eX=/^'([^]*?)'?$/,e$=/''/g,eY=/[a-zA-Z]/;function eQ(e,t,n){(0,ei.Z)(2,arguments);var r,o,i,a,l,c,s,u,d,f,p,h,m,g,v,y,b,x,w=String(t),S=null!==(r=null!==(o=null==n?void 0:n.locale)&&void 0!==o?o:ek.locale)&&void 0!==r?r:eV,k=(0,em.Z)(null!==(i=null!==(a=null!==(l=null!==(c=null==n?void 0:n.firstWeekContainsDate)&&void 0!==c?c:null==n?void 0:null===(s=n.locale)||void 0===s?void 0:null===(u=s.options)||void 0===u?void 0:u.firstWeekContainsDate)&&void 0!==l?l:ek.firstWeekContainsDate)&&void 0!==a?a:null===(d=ek.locale)||void 0===d?void 0:null===(f=d.options)||void 0===f?void 0:f.firstWeekContainsDate)&&void 0!==i?i:1);if(!(k>=1&&k<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var E=(0,em.Z)(null!==(p=null!==(h=null!==(m=null!==(g=null==n?void 0:n.weekStartsOn)&&void 0!==g?g:null==n?void 0:null===(v=n.locale)||void 0===v?void 0:null===(y=v.options)||void 0===y?void 0:y.weekStartsOn)&&void 0!==m?m:ek.weekStartsOn)&&void 0!==h?h:null===(b=ek.locale)||void 0===b?void 0:null===(x=b.options)||void 0===x?void 0:x.weekStartsOn)&&void 0!==p?p:0);if(!(E>=0&&E<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");if(!S.localize)throw RangeError("locale must contain localize property");if(!S.formatLong)throw RangeError("locale must contain formatLong property");var C=(0,eo.Z)(e);if(!function(e){return(0,ei.Z)(1,arguments),(!!ex(e)||"number"==typeof e)&&!isNaN(Number((0,eo.Z)(e)))}(C))throw RangeError("Invalid time value");var O=eD(C),j=function(e,t){return(0,ei.Z)(2,arguments),function(e,t){return(0,ei.Z)(2,arguments),new Date((0,eo.Z)(e).getTime()+(0,em.Z)(t))}(e,-(0,em.Z)(t))}(C,O),P={firstWeekContainsDate:k,weekStartsOn:E,locale:S,_originalDate:C};return w.match(eG).map(function(e){var t=e[0];return"p"===t||"P"===t?(0,e_[t])(e,S.formatLong):e}).join("").match(eU).map(function(r){if("''"===r)return"'";var o,i=r[0];if("'"===i)return(o=r.match(eX))?o[1].replace(e$,"'"):r;var a=eR[i];if(a)return null!=n&&n.useAdditionalWeekYearTokens||-1===eL.indexOf(r)||ez(r,t,String(e)),null!=n&&n.useAdditionalDayOfYearTokens||-1===eZ.indexOf(r)||ez(r,t,String(e)),a(j,r,S.localize,P);if(i.match(eY))throw RangeError("Format string contains an unescaped latin alphabet character `"+i+"`");return r}).join("")}var eJ=n(1153);let e0=(0,eJ.fn)("DateRangePicker"),e1=(e,t,n,r)=>{var o;if(n&&(e=null===(o=r.get(n))||void 0===o?void 0:o.from),e)return ea(e&&!t?e:ef([e,t]))},e2=(e,t,n,r)=>{var o,i;if(n&&(e=ea(null!==(i=null===(o=r.get(n))||void 0===o?void 0:o.to)&&void 0!==i?i:el())),e)return ea(e&&!t?e:ep([e,t]))},e6=[{value:"tdy",text:"Today",from:el()},{value:"w",text:"Last 7 days",from:ey(el(),{days:7})},{value:"t",text:"Last 30 days",from:ey(el(),{days:30})},{value:"m",text:"Month to Date",from:ec(el())},{value:"y",text:"Year to Date",from:eb(el())}],e4=(e,t,n,r)=>{let o=(null==n?void 0:n.code)||"en-US";if(!e&&!t)return"";if(e&&!t)return r?eQ(e,r):e.toLocaleDateString(o,{year:"numeric",month:"short",day:"numeric"});if(e&&t){if(function(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,eo.Z)(t);return n.getTime()===r.getTime()}(e,t))return r?eQ(e,r):e.toLocaleDateString(o,{year:"numeric",month:"short",day:"numeric"});if(e.getMonth()===t.getMonth()&&e.getFullYear()===t.getFullYear())return r?"".concat(eQ(e,r)," - ").concat(eQ(t,r)):"".concat(e.toLocaleDateString(o,{month:"short",day:"numeric"})," - \n ").concat(t.getDate(),", ").concat(t.getFullYear());{if(r)return"".concat(eQ(e,r)," - ").concat(eQ(t,r));let n={year:"numeric",month:"short",day:"numeric"};return"".concat(e.toLocaleDateString(o,n)," - \n ").concat(t.toLocaleDateString(o,n))}}return""};function e3(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getMonth();return t.setFullYear(t.getFullYear(),n+1,0),t.setHours(23,59,59,999),t}function e5(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,em.Z)(t),o=n.getFullYear(),i=n.getDate(),a=new Date(0);a.setFullYear(o,r,15),a.setHours(0,0,0,0);var l=function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getFullYear(),r=t.getMonth(),o=new Date(0);return o.setFullYear(n,r+1,0),o.setHours(0,0,0,0),o.getDate()}(a);return n.setMonth(r,Math.min(i,l)),n}function e8(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,em.Z)(t);return isNaN(n.getTime())?new Date(NaN):(n.setFullYear(r),n)}function e7(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,eo.Z)(t);return 12*(n.getFullYear()-r.getFullYear())+(n.getMonth()-r.getMonth())}function e9(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,eo.Z)(t);return n.getFullYear()===r.getFullYear()&&n.getMonth()===r.getMonth()}function te(e,t){(0,ei.Z)(2,arguments);var n=(0,eo.Z)(e),r=(0,eo.Z)(t);return n.getTime()=0&&u<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");var d=(0,eo.Z)(e),f=d.getDay();return d.setDate(d.getDate()-((fr.getTime()}function ti(e,t){(0,ei.Z)(2,arguments);var n=ea(e),r=ea(t);return Math.round((n.getTime()-eD(n)-(r.getTime()-eD(r)))/864e5)}function ta(e,t){(0,ei.Z)(2,arguments);var n=(0,em.Z)(t);return(0,eh.Z)(e,7*n)}function tl(e,t){(0,ei.Z)(2,arguments);var n=(0,em.Z)(t);return(0,ev.Z)(e,12*n)}function tc(e,t){(0,ei.Z)(1,arguments);var n,r,o,i,a,l,c,s,u=(0,em.Z)(null!==(n=null!==(r=null!==(o=null!==(i=null==t?void 0:t.weekStartsOn)&&void 0!==i?i:null==t?void 0:null===(a=t.locale)||void 0===a?void 0:null===(l=a.options)||void 0===l?void 0:l.weekStartsOn)&&void 0!==o?o:ek.weekStartsOn)&&void 0!==r?r:null===(c=ek.locale)||void 0===c?void 0:null===(s=c.options)||void 0===s?void 0:s.weekStartsOn)&&void 0!==n?n:0);if(!(u>=0&&u<=6))throw RangeError("weekStartsOn must be between 0 and 6 inclusively");var d=(0,eo.Z)(e),f=d.getDay();return d.setDate(d.getDate()+((fe7(l,a)&&(a=(0,ev.Z)(l,-1*((void 0===s?1:s)-1))),c&&0>e7(a,c)&&(a=c),u=ec(a),f=t.month,h=(p=(0,d.useState)(u))[0],m=[void 0===f?h:f,p[1]])[0],v=m[1],[g,function(e){if(!t.disableNavigation){var n,r=ec(e);v(r),null===(n=t.onMonthChange)||void 0===n||n.call(t,r)}}]),x=b[0],w=b[1],S=function(e,t){for(var n=t.reverseMonths,r=t.numberOfMonths,o=ec(e),i=e7(ec((0,ev.Z)(o,r)),o),a=[],l=0;l=e7(i,n)))return(0,ev.Z)(i,-(r?void 0===o?1:o:1))}}(x,y),C=function(e){return S.some(function(t){return e9(e,t)})};return th.jsx(tN.Provider,{value:{currentMonth:x,displayMonths:S,goToMonth:w,goToDate:function(e,t){C(e)||(t&&te(e,t)?w((0,ev.Z)(e,1+-1*y.numberOfMonths)):w(e))},previousMonth:E,nextMonth:k,isDateDisplayed:C},children:e.children})}function tI(){var e=(0,d.useContext)(tN);if(!e)throw Error("useNavigation must be used within a NavigationProvider");return e}function tR(e){var t,n=tk(),r=n.classNames,o=n.styles,i=n.components,a=tI().goToMonth,l=function(t){a((0,ev.Z)(t,e.displayIndex?-e.displayIndex:0))},c=null!==(t=null==i?void 0:i.CaptionLabel)&&void 0!==t?t:tE,s=th.jsx(c,{id:e.id,displayMonth:e.displayMonth});return th.jsxs("div",{className:r.caption_dropdowns,style:o.caption_dropdowns,children:[th.jsx("div",{className:r.vhidden,children:s}),th.jsx(tj,{onChange:l,displayMonth:e.displayMonth}),th.jsx(tP,{onChange:l,displayMonth:e.displayMonth})]})}function tT(e){return th.jsx("svg",tu({width:"16px",height:"16px",viewBox:"0 0 120 120"},e,{children:th.jsx("path",{d:"M69.490332,3.34314575 C72.6145263,0.218951416 77.6798462,0.218951416 80.8040405,3.34314575 C83.8617626,6.40086786 83.9268205,11.3179931 80.9992143,14.4548388 L80.8040405,14.6568542 L35.461,60 L80.8040405,105.343146 C83.8617626,108.400868 83.9268205,113.317993 80.9992143,116.454839 L80.8040405,116.656854 C77.7463184,119.714576 72.8291931,119.779634 69.6923475,116.852028 L69.490332,116.656854 L18.490332,65.6568542 C15.4326099,62.5991321 15.367552,57.6820069 18.2951583,54.5451612 L18.490332,54.3431458 L69.490332,3.34314575 Z",fill:"currentColor",fillRule:"nonzero"})}))}function tA(e){return th.jsx("svg",tu({width:"16px",height:"16px",viewBox:"0 0 120 120"},e,{children:th.jsx("path",{d:"M49.8040405,3.34314575 C46.6798462,0.218951416 41.6145263,0.218951416 38.490332,3.34314575 C35.4326099,6.40086786 35.367552,11.3179931 38.2951583,14.4548388 L38.490332,14.6568542 L83.8333725,60 L38.490332,105.343146 C35.4326099,108.400868 35.367552,113.317993 38.2951583,116.454839 L38.490332,116.656854 C41.5480541,119.714576 46.4651794,119.779634 49.602025,116.852028 L49.8040405,116.656854 L100.804041,65.6568542 C103.861763,62.5991321 103.926821,57.6820069 100.999214,54.5451612 L100.804041,54.3431458 L49.8040405,3.34314575 Z",fill:"currentColor"})}))}var t_=(0,d.forwardRef)(function(e,t){var n=tk(),r=n.classNames,o=n.styles,i=[r.button_reset,r.button];e.className&&i.push(e.className);var a=i.join(" "),l=tu(tu({},o.button_reset),o.button);return e.style&&Object.assign(l,e.style),th.jsx("button",tu({},e,{ref:t,type:"button",className:a,style:l}))});function tD(e){var t,n,r=tk(),o=r.dir,i=r.locale,a=r.classNames,l=r.styles,c=r.labels,s=c.labelPrevious,u=c.labelNext,d=r.components;if(!e.nextMonth&&!e.previousMonth)return th.jsx(th.Fragment,{});var f=s(e.previousMonth,{locale:i}),p=[a.nav_button,a.nav_button_previous].join(" "),h=u(e.nextMonth,{locale:i}),m=[a.nav_button,a.nav_button_next].join(" "),g=null!==(t=null==d?void 0:d.IconRight)&&void 0!==t?t:tA,v=null!==(n=null==d?void 0:d.IconLeft)&&void 0!==n?n:tT;return th.jsxs("div",{className:a.nav,style:l.nav,children:[!e.hidePrevious&&th.jsx(t_,{name:"previous-month","aria-label":f,className:p,style:l.nav_button_previous,disabled:!e.previousMonth,onClick:e.onPreviousClick,children:"rtl"===o?th.jsx(g,{className:a.nav_icon,style:l.nav_icon}):th.jsx(v,{className:a.nav_icon,style:l.nav_icon})}),!e.hideNext&&th.jsx(t_,{name:"next-month","aria-label":h,className:m,style:l.nav_button_next,disabled:!e.nextMonth,onClick:e.onNextClick,children:"rtl"===o?th.jsx(v,{className:a.nav_icon,style:l.nav_icon}):th.jsx(g,{className:a.nav_icon,style:l.nav_icon})})]})}function tZ(e){var t=tk().numberOfMonths,n=tI(),r=n.previousMonth,o=n.nextMonth,i=n.goToMonth,a=n.displayMonths,l=a.findIndex(function(t){return e9(e.displayMonth,t)}),c=0===l,s=l===a.length-1;return th.jsx(tD,{displayMonth:e.displayMonth,hideNext:t>1&&(c||!s),hidePrevious:t>1&&(s||!c),nextMonth:o,previousMonth:r,onPreviousClick:function(){r&&i(r)},onNextClick:function(){o&&i(o)}})}function tL(e){var t,n,r=tk(),o=r.classNames,i=r.disableNavigation,a=r.styles,l=r.captionLayout,c=r.components,s=null!==(t=null==c?void 0:c.CaptionLabel)&&void 0!==t?t:tE;return n=i?th.jsx(s,{id:e.id,displayMonth:e.displayMonth}):"dropdown"===l?th.jsx(tR,{displayMonth:e.displayMonth,id:e.id}):"dropdown-buttons"===l?th.jsxs(th.Fragment,{children:[th.jsx(tR,{displayMonth:e.displayMonth,displayIndex:e.displayIndex,id:e.id}),th.jsx(tZ,{displayMonth:e.displayMonth,displayIndex:e.displayIndex,id:e.id})]}):th.jsxs(th.Fragment,{children:[th.jsx(s,{id:e.id,displayMonth:e.displayMonth,displayIndex:e.displayIndex}),th.jsx(tZ,{displayMonth:e.displayMonth,id:e.id})]}),th.jsx("div",{className:o.caption,style:a.caption,children:n})}function tz(e){var t=tk(),n=t.footer,r=t.styles,o=t.classNames.tfoot;return n?th.jsx("tfoot",{className:o,style:r.tfoot,children:th.jsx("tr",{children:th.jsx("td",{colSpan:8,children:n})})}):th.jsx(th.Fragment,{})}function tB(){var e=tk(),t=e.classNames,n=e.styles,r=e.showWeekNumber,o=e.locale,i=e.weekStartsOn,a=e.ISOWeek,l=e.formatters.formatWeekdayName,c=e.labels.labelWeekday,s=function(e,t,n){for(var r=n?tn(new Date):tt(new Date,{locale:e,weekStartsOn:t}),o=[],i=0;i<7;i++){var a=(0,eh.Z)(r,i);o.push(a)}return o}(o,i,a);return th.jsxs("tr",{style:n.head_row,className:t.head_row,children:[r&&th.jsx("td",{style:n.head_cell,className:t.head_cell}),s.map(function(e,r){return th.jsx("th",{scope:"col",className:t.head_cell,style:n.head_cell,"aria-label":c(e,{locale:o}),children:l(e,{locale:o})},r)})]})}function tF(){var e,t=tk(),n=t.classNames,r=t.styles,o=t.components,i=null!==(e=null==o?void 0:o.HeadRow)&&void 0!==e?e:tB;return th.jsx("thead",{style:r.head,className:n.head,children:th.jsx(i,{})})}function tH(e){var t=tk(),n=t.locale,r=t.formatters.formatDay;return th.jsx(th.Fragment,{children:r(e.date,{locale:n})})}var tq=(0,d.createContext)(void 0);function tW(e){return tm(e.initialProps)?th.jsx(tK,{initialProps:e.initialProps,children:e.children}):th.jsx(tq.Provider,{value:{selected:void 0,modifiers:{disabled:[]}},children:e.children})}function tK(e){var t=e.initialProps,n=e.children,r=t.selected,o=t.min,i=t.max,a={disabled:[]};return r&&a.disabled.push(function(e){var t=i&&r.length>i-1,n=r.some(function(t){return tr(t,e)});return!!(t&&!n)}),th.jsx(tq.Provider,{value:{selected:r,onDayClick:function(e,n,a){if(null===(l=t.onDayClick)||void 0===l||l.call(t,e,n,a),(!n.selected||!o||(null==r?void 0:r.length)!==o)&&(n.selected||!i||(null==r?void 0:r.length)!==i)){var l,c,s=r?td([],r,!0):[];if(n.selected){var u=s.findIndex(function(t){return tr(e,t)});s.splice(u,1)}else s.push(e);null===(c=t.onSelect)||void 0===c||c.call(t,s,e,n,a)}},modifiers:a},children:n})}function tV(){var e=(0,d.useContext)(tq);if(!e)throw Error("useSelectMultiple must be used within a SelectMultipleProvider");return e}var tU=(0,d.createContext)(void 0);function tG(e){return tg(e.initialProps)?th.jsx(tX,{initialProps:e.initialProps,children:e.children}):th.jsx(tU.Provider,{value:{selected:void 0,modifiers:{range_start:[],range_end:[],range_middle:[],disabled:[]}},children:e.children})}function tX(e){var t=e.initialProps,n=e.children,r=t.selected,o=r||{},i=o.from,a=o.to,l=t.min,c=t.max,s={range_start:[],range_end:[],range_middle:[],disabled:[]};if(i?(s.range_start=[i],a?(s.range_end=[a],tr(i,a)||(s.range_middle=[{after:i,before:a}])):s.range_end=[i]):a&&(s.range_start=[a],s.range_end=[a]),l&&(i&&!a&&s.disabled.push({after:eg(i,l-1),before:(0,eh.Z)(i,l-1)}),i&&a&&s.disabled.push({after:i,before:(0,eh.Z)(i,l-1)}),!i&&a&&s.disabled.push({after:eg(a,l-1),before:(0,eh.Z)(a,l-1)})),c){if(i&&!a&&(s.disabled.push({before:(0,eh.Z)(i,-c+1)}),s.disabled.push({after:(0,eh.Z)(i,c-1)})),i&&a){var u=c-(ti(a,i)+1);s.disabled.push({before:eg(i,u)}),s.disabled.push({after:(0,eh.Z)(a,u)})}!i&&a&&(s.disabled.push({before:(0,eh.Z)(a,-c+1)}),s.disabled.push({after:(0,eh.Z)(a,c-1)}))}return th.jsx(tU.Provider,{value:{selected:r,onDayClick:function(e,n,o){null===(c=t.onDayClick)||void 0===c||c.call(t,e,n,o);var i,a,l,c,s,u=(a=(i=r||{}).from,l=i.to,a&&l?tr(l,e)&&tr(a,e)?void 0:tr(l,e)?{from:l,to:void 0}:tr(a,e)?void 0:to(a,e)?{from:e,to:l}:{from:a,to:e}:l?to(e,l)?{from:l,to:e}:{from:e,to:l}:a?te(e,a)?{from:e,to:a}:{from:a,to:e}:{from:e,to:void 0});null===(s=t.onSelect)||void 0===s||s.call(t,u,e,n,o)},modifiers:s},children:n})}function t$(){var e=(0,d.useContext)(tU);if(!e)throw Error("useSelectRange must be used within a SelectRangeProvider");return e}function tY(e){return Array.isArray(e)?td([],e,!0):void 0!==e?[e]:[]}(l=s||(s={})).Outside="outside",l.Disabled="disabled",l.Selected="selected",l.Hidden="hidden",l.Today="today",l.RangeStart="range_start",l.RangeEnd="range_end",l.RangeMiddle="range_middle";var tQ=s.Selected,tJ=s.Disabled,t0=s.Hidden,t1=s.Today,t2=s.RangeEnd,t6=s.RangeMiddle,t4=s.RangeStart,t3=s.Outside,t5=(0,d.createContext)(void 0);function t8(e){var t,n,r,o=tk(),i=tV(),a=t$(),l=((t={})[tQ]=tY(o.selected),t[tJ]=tY(o.disabled),t[t0]=tY(o.hidden),t[t1]=[o.today],t[t2]=[],t[t6]=[],t[t4]=[],t[t3]=[],o.fromDate&&t[tJ].push({before:o.fromDate}),o.toDate&&t[tJ].push({after:o.toDate}),tm(o)?t[tJ]=t[tJ].concat(i.modifiers[tJ]):tg(o)&&(t[tJ]=t[tJ].concat(a.modifiers[tJ]),t[t4]=a.modifiers[t4],t[t6]=a.modifiers[t6],t[t2]=a.modifiers[t2]),t),c=(n=o.modifiers,r={},Object.entries(n).forEach(function(e){var t=e[0],n=e[1];r[t]=tY(n)}),r),s=tu(tu({},l),c);return th.jsx(t5.Provider,{value:s,children:e.children})}function t7(){var e=(0,d.useContext)(t5);if(!e)throw Error("useModifiers must be used within a ModifiersProvider");return e}function t9(e,t,n){var r=Object.keys(t).reduce(function(n,r){return t[r].some(function(t){if("boolean"==typeof t)return t;if(ex(t))return tr(e,t);if(Array.isArray(t)&&t.every(ex))return t.includes(e);if(t&&"object"==typeof t&&"from"in t)return r=t.from,o=t.to,r&&o?(0>ti(o,r)&&(r=(n=[o,r])[0],o=n[1]),ti(e,r)>=0&&ti(o,e)>=0):o?tr(o,e):!!r&&tr(r,e);if(t&&"object"==typeof t&&"dayOfWeek"in t)return t.dayOfWeek.includes(e.getDay());if(t&&"object"==typeof t&&"before"in t&&"after"in t){var n,r,o,i=ti(t.before,e),a=ti(t.after,e),l=i>0,c=a<0;return to(t.before,t.after)?c&&l:l||c}return t&&"object"==typeof t&&"after"in t?ti(e,t.after)>0:t&&"object"==typeof t&&"before"in t?ti(t.before,e)>0:"function"==typeof t&&t(e)})&&n.push(r),n},[]),o={};return r.forEach(function(e){return o[e]=!0}),n&&!e9(e,n)&&(o.outside=!0),o}var ne=(0,d.createContext)(void 0);function nt(e){var t=tI(),n=t7(),r=(0,d.useState)(),o=r[0],i=r[1],a=(0,d.useState)(),l=a[0],c=a[1],s=function(e,t){for(var n,r,o=ec(e[0]),i=e3(e[e.length-1]),a=o;a<=i;){var l=t9(a,t);if(!(!l.disabled&&!l.hidden)){a=(0,eh.Z)(a,1);continue}if(l.selected)return a;l.today&&!r&&(r=a),n||(n=a),a=(0,eh.Z)(a,1)}return r||n}(t.displayMonths,n),u=(null!=o?o:l&&t.isDateDisplayed(l))?l:s,f=function(e){i(e)},p=tk(),h=function(e,r){if(o){var i=function e(t,n){var r=n.moveBy,o=n.direction,i=n.context,a=n.modifiers,l=n.retry,c=void 0===l?{count:0,lastFocused:t}:l,s=i.weekStartsOn,u=i.fromDate,d=i.toDate,f=i.locale,p=({day:eh.Z,week:ta,month:ev.Z,year:tl,startOfWeek:function(e){return i.ISOWeek?tn(e):tt(e,{locale:f,weekStartsOn:s})},endOfWeek:function(e){return i.ISOWeek?ts(e):tc(e,{locale:f,weekStartsOn:s})}})[r](t,"after"===o?1:-1);"before"===o&&u?p=ef([u,p]):"after"===o&&d&&(p=ep([d,p]));var h=!0;if(a){var m=t9(p,a);h=!m.disabled&&!m.hidden}return h?p:c.count>365?c.lastFocused:e(p,{moveBy:r,direction:o,context:i,modifiers:a,retry:tu(tu({},c),{count:c.count+1})})}(o,{moveBy:e,direction:r,context:p,modifiers:n});tr(o,i)||(t.goToDate(i,o),f(i))}};return th.jsx(ne.Provider,{value:{focusedDay:o,focusTarget:u,blur:function(){c(o),i(void 0)},focus:f,focusDayAfter:function(){return h("day","after")},focusDayBefore:function(){return h("day","before")},focusWeekAfter:function(){return h("week","after")},focusWeekBefore:function(){return h("week","before")},focusMonthBefore:function(){return h("month","before")},focusMonthAfter:function(){return h("month","after")},focusYearBefore:function(){return h("year","before")},focusYearAfter:function(){return h("year","after")},focusStartOfWeek:function(){return h("startOfWeek","before")},focusEndOfWeek:function(){return h("endOfWeek","after")}},children:e.children})}function nn(){var e=(0,d.useContext)(ne);if(!e)throw Error("useFocusContext must be used within a FocusProvider");return e}var nr=(0,d.createContext)(void 0);function no(e){return tv(e.initialProps)?th.jsx(ni,{initialProps:e.initialProps,children:e.children}):th.jsx(nr.Provider,{value:{selected:void 0},children:e.children})}function ni(e){var t=e.initialProps,n=e.children,r={selected:t.selected,onDayClick:function(e,n,r){var o,i,a;if(null===(o=t.onDayClick)||void 0===o||o.call(t,e,n,r),n.selected&&!t.required){null===(i=t.onSelect)||void 0===i||i.call(t,void 0,e,n,r);return}null===(a=t.onSelect)||void 0===a||a.call(t,e,e,n,r)}};return th.jsx(nr.Provider,{value:r,children:n})}function na(){var e=(0,d.useContext)(nr);if(!e)throw Error("useSelectSingle must be used within a SelectSingleProvider");return e}function nl(e){var t,n,r,o,i,a,l,c,u,f,p,h,m,g,v,y,b,x,w,S,k,E,C,O,j,P,N,M,I,R,T,A,_,D,Z,L,z,B,F,H,q,W,K=(0,d.useRef)(null),V=(t=e.date,n=e.displayMonth,a=tk(),l=nn(),c=t9(t,t7(),n),u=tk(),f=na(),p=tV(),h=t$(),g=(m=nn()).focusDayAfter,v=m.focusDayBefore,y=m.focusWeekAfter,b=m.focusWeekBefore,x=m.blur,w=m.focus,S=m.focusMonthBefore,k=m.focusMonthAfter,E=m.focusYearBefore,C=m.focusYearAfter,O=m.focusStartOfWeek,j=m.focusEndOfWeek,P={onClick:function(e){var n,r,o,i;tv(u)?null===(n=f.onDayClick)||void 0===n||n.call(f,t,c,e):tm(u)?null===(r=p.onDayClick)||void 0===r||r.call(p,t,c,e):tg(u)?null===(o=h.onDayClick)||void 0===o||o.call(h,t,c,e):null===(i=u.onDayClick)||void 0===i||i.call(u,t,c,e)},onFocus:function(e){var n;w(t),null===(n=u.onDayFocus)||void 0===n||n.call(u,t,c,e)},onBlur:function(e){var n;x(),null===(n=u.onDayBlur)||void 0===n||n.call(u,t,c,e)},onKeyDown:function(e){var n;switch(e.key){case"ArrowLeft":e.preventDefault(),e.stopPropagation(),"rtl"===u.dir?g():v();break;case"ArrowRight":e.preventDefault(),e.stopPropagation(),"rtl"===u.dir?v():g();break;case"ArrowDown":e.preventDefault(),e.stopPropagation(),y();break;case"ArrowUp":e.preventDefault(),e.stopPropagation(),b();break;case"PageUp":e.preventDefault(),e.stopPropagation(),e.shiftKey?E():S();break;case"PageDown":e.preventDefault(),e.stopPropagation(),e.shiftKey?C():k();break;case"Home":e.preventDefault(),e.stopPropagation(),O();break;case"End":e.preventDefault(),e.stopPropagation(),j()}null===(n=u.onDayKeyDown)||void 0===n||n.call(u,t,c,e)},onKeyUp:function(e){var n;null===(n=u.onDayKeyUp)||void 0===n||n.call(u,t,c,e)},onMouseEnter:function(e){var n;null===(n=u.onDayMouseEnter)||void 0===n||n.call(u,t,c,e)},onMouseLeave:function(e){var n;null===(n=u.onDayMouseLeave)||void 0===n||n.call(u,t,c,e)},onPointerEnter:function(e){var n;null===(n=u.onDayPointerEnter)||void 0===n||n.call(u,t,c,e)},onPointerLeave:function(e){var n;null===(n=u.onDayPointerLeave)||void 0===n||n.call(u,t,c,e)},onTouchCancel:function(e){var n;null===(n=u.onDayTouchCancel)||void 0===n||n.call(u,t,c,e)},onTouchEnd:function(e){var n;null===(n=u.onDayTouchEnd)||void 0===n||n.call(u,t,c,e)},onTouchMove:function(e){var n;null===(n=u.onDayTouchMove)||void 0===n||n.call(u,t,c,e)},onTouchStart:function(e){var n;null===(n=u.onDayTouchStart)||void 0===n||n.call(u,t,c,e)}},N=tk(),M=na(),I=tV(),R=t$(),T=tv(N)?M.selected:tm(N)?I.selected:tg(N)?R.selected:void 0,A=!!(a.onDayClick||"default"!==a.mode),(0,d.useEffect)(function(){var e;!c.outside&&l.focusedDay&&A&&tr(l.focusedDay,t)&&(null===(e=K.current)||void 0===e||e.focus())},[l.focusedDay,t,K,A,c.outside]),D=(_=[a.classNames.day],Object.keys(c).forEach(function(e){var t=a.modifiersClassNames[e];if(t)_.push(t);else if(Object.values(s).includes(e)){var n=a.classNames["day_".concat(e)];n&&_.push(n)}}),_).join(" "),Z=tu({},a.styles.day),Object.keys(c).forEach(function(e){var t;Z=tu(tu({},Z),null===(t=a.modifiersStyles)||void 0===t?void 0:t[e])}),L=Z,z=!!(c.outside&&!a.showOutsideDays||c.hidden),B=null!==(i=null===(o=a.components)||void 0===o?void 0:o.DayContent)&&void 0!==i?i:tH,F={style:L,className:D,children:th.jsx(B,{date:t,displayMonth:n,activeModifiers:c}),role:"gridcell"},H=l.focusTarget&&tr(l.focusTarget,t)&&!c.outside,q=l.focusedDay&&tr(l.focusedDay,t),W=tu(tu(tu({},F),((r={disabled:c.disabled,role:"gridcell"})["aria-selected"]=c.selected,r.tabIndex=q||H?0:-1,r)),P),{isButton:A,isHidden:z,activeModifiers:c,selectedDays:T,buttonProps:W,divProps:F});return V.isHidden?th.jsx("div",{role:"gridcell"}):V.isButton?th.jsx(t_,tu({name:"day",ref:K},V.buttonProps)):th.jsx("div",tu({},V.divProps))}function nc(e){var t=e.number,n=e.dates,r=tk(),o=r.onWeekNumberClick,i=r.styles,a=r.classNames,l=r.locale,c=r.labels.labelWeekNumber,s=(0,r.formatters.formatWeekNumber)(Number(t),{locale:l});if(!o)return th.jsx("span",{className:a.weeknumber,style:i.weeknumber,children:s});var u=c(Number(t),{locale:l});return th.jsx(t_,{name:"week-number","aria-label":u,className:a.weeknumber,style:i.weeknumber,onClick:function(e){o(t,n,e)},children:s})}function ns(e){var t,n,r,o=tk(),i=o.styles,a=o.classNames,l=o.showWeekNumber,c=o.components,s=null!==(t=null==c?void 0:c.Day)&&void 0!==t?t:nl,u=null!==(n=null==c?void 0:c.WeekNumber)&&void 0!==n?n:nc;return l&&(r=th.jsx("td",{className:a.cell,style:i.cell,children:th.jsx(u,{number:e.weekNumber,dates:e.dates})})),th.jsxs("tr",{className:a.row,style:i.row,children:[r,e.dates.map(function(t){return th.jsx("td",{className:a.cell,style:i.cell,role:"presentation",children:th.jsx(s,{displayMonth:e.displayMonth,date:t})},function(e){return(0,ei.Z)(1,arguments),Math.floor(function(e){return(0,ei.Z)(1,arguments),(0,eo.Z)(e).getTime()}(e)/1e3)}(t))})]})}function nu(e,t,n){for(var r=(null==n?void 0:n.ISOWeek)?ts(t):tc(t,n),o=(null==n?void 0:n.ISOWeek)?tn(e):tt(e,n),i=ti(r,o),a=[],l=0;l<=i;l++)a.push((0,eh.Z)(o,l));return a.reduce(function(e,t){var r=(null==n?void 0:n.ISOWeek)?function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e);return Math.round((tn(t).getTime()-(function(e){(0,ei.Z)(1,arguments);var t=function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getFullYear(),r=new Date(0);r.setFullYear(n+1,0,4),r.setHours(0,0,0,0);var o=tn(r),i=new Date(0);i.setFullYear(n,0,4),i.setHours(0,0,0,0);var a=tn(i);return t.getTime()>=o.getTime()?n+1:t.getTime()>=a.getTime()?n:n-1}(e),n=new Date(0);return n.setFullYear(t,0,4),n.setHours(0,0,0,0),tn(n)})(t).getTime())/6048e5)+1}(t):function(e,t){(0,ei.Z)(1,arguments);var n=(0,eo.Z)(e);return Math.round((tt(n,t).getTime()-(function(e,t){(0,ei.Z)(1,arguments);var n,r,o,i,a,l,c,s,u=(0,em.Z)(null!==(n=null!==(r=null!==(o=null!==(i=null==t?void 0:t.firstWeekContainsDate)&&void 0!==i?i:null==t?void 0:null===(a=t.locale)||void 0===a?void 0:null===(l=a.options)||void 0===l?void 0:l.firstWeekContainsDate)&&void 0!==o?o:ek.firstWeekContainsDate)&&void 0!==r?r:null===(c=ek.locale)||void 0===c?void 0:null===(s=c.options)||void 0===s?void 0:s.firstWeekContainsDate)&&void 0!==n?n:1),d=function(e,t){(0,ei.Z)(1,arguments);var n,r,o,i,a,l,c,s,u=(0,eo.Z)(e),d=u.getFullYear(),f=(0,em.Z)(null!==(n=null!==(r=null!==(o=null!==(i=null==t?void 0:t.firstWeekContainsDate)&&void 0!==i?i:null==t?void 0:null===(a=t.locale)||void 0===a?void 0:null===(l=a.options)||void 0===l?void 0:l.firstWeekContainsDate)&&void 0!==o?o:ek.firstWeekContainsDate)&&void 0!==r?r:null===(c=ek.locale)||void 0===c?void 0:null===(s=c.options)||void 0===s?void 0:s.firstWeekContainsDate)&&void 0!==n?n:1);if(!(f>=1&&f<=7))throw RangeError("firstWeekContainsDate must be between 1 and 7 inclusively");var p=new Date(0);p.setFullYear(d+1,0,f),p.setHours(0,0,0,0);var h=tt(p,t),m=new Date(0);m.setFullYear(d,0,f),m.setHours(0,0,0,0);var g=tt(m,t);return u.getTime()>=h.getTime()?d+1:u.getTime()>=g.getTime()?d:d-1}(e,t),f=new Date(0);return f.setFullYear(d,0,u),f.setHours(0,0,0,0),tt(f,t)})(n,t).getTime())/6048e5)+1}(t,n),o=e.find(function(e){return e.weekNumber===r});return o?o.dates.push(t):e.push({weekNumber:r,dates:[t]}),e},[])}function nd(e){var t,n,r,o=tk(),i=o.locale,a=o.classNames,l=o.styles,c=o.hideHead,s=o.fixedWeeks,u=o.components,d=o.weekStartsOn,f=o.firstWeekContainsDate,p=o.ISOWeek,h=function(e,t){var n=nu(ec(e),e3(e),t);if(null==t?void 0:t.useFixedWeeks){var r=function(e,t){return(0,ei.Z)(1,arguments),function(e,t,n){(0,ei.Z)(2,arguments);var r=tt(e,n),o=tt(t,n);return Math.round((r.getTime()-eD(r)-(o.getTime()-eD(o)))/6048e5)}(function(e){(0,ei.Z)(1,arguments);var t=(0,eo.Z)(e),n=t.getMonth();return t.setFullYear(t.getFullYear(),n+1,0),t.setHours(0,0,0,0),t}(e),ec(e),t)+1}(e,t);if(r<6){var o=n[n.length-1],i=o.dates[o.dates.length-1],a=ta(i,6-r),l=nu(ta(i,1),a,t);n.push.apply(n,l)}}return n}(e.displayMonth,{useFixedWeeks:!!s,ISOWeek:p,locale:i,weekStartsOn:d,firstWeekContainsDate:f}),m=null!==(t=null==u?void 0:u.Head)&&void 0!==t?t:tF,g=null!==(n=null==u?void 0:u.Row)&&void 0!==n?n:ns,v=null!==(r=null==u?void 0:u.Footer)&&void 0!==r?r:tz;return th.jsxs("table",{id:e.id,className:a.table,style:l.table,role:"grid","aria-labelledby":e["aria-labelledby"],children:[!c&&th.jsx(m,{}),th.jsx("tbody",{className:a.tbody,style:l.tbody,children:h.map(function(t){return th.jsx(g,{displayMonth:e.displayMonth,dates:t.dates,weekNumber:t.weekNumber},t.weekNumber)})}),th.jsx(v,{displayMonth:e.displayMonth})]})}var nf="undefined"!=typeof window&&window.document&&window.document.createElement?d.useLayoutEffect:d.useEffect,np=!1,nh=0;function nm(){return"react-day-picker-".concat(++nh)}function ng(e){var t,n,r,o,i,a,l,c,s=tk(),u=s.dir,f=s.classNames,p=s.styles,h=s.components,m=tI().displayMonths,g=(r=null!=(t=s.id?"".concat(s.id,"-").concat(e.displayIndex):void 0)?t:np?nm():null,i=(o=(0,d.useState)(r))[0],a=o[1],nf(function(){null===i&&a(nm())},[]),(0,d.useEffect)(function(){!1===np&&(np=!0)},[]),null!==(n=null!=t?t:i)&&void 0!==n?n:void 0),v=s.id?"".concat(s.id,"-grid-").concat(e.displayIndex):void 0,y=[f.month],b=p.month,x=0===e.displayIndex,w=e.displayIndex===m.length-1,S=!x&&!w;"rtl"===u&&(w=(l=[x,w])[0],x=l[1]),x&&(y.push(f.caption_start),b=tu(tu({},b),p.caption_start)),w&&(y.push(f.caption_end),b=tu(tu({},b),p.caption_end)),S&&(y.push(f.caption_between),b=tu(tu({},b),p.caption_between));var k=null!==(c=null==h?void 0:h.Caption)&&void 0!==c?c:tL;return th.jsxs("div",{className:y.join(" "),style:b,children:[th.jsx(k,{id:g,displayMonth:e.displayMonth,displayIndex:e.displayIndex}),th.jsx(nd,{id:v,"aria-labelledby":g,displayMonth:e.displayMonth})]},e.displayIndex)}function nv(e){var t=tk(),n=t.classNames,r=t.styles;return th.jsx("div",{className:n.months,style:r.months,children:e.children})}function ny(e){var t,n,r=e.initialProps,o=tk(),i=nn(),a=tI(),l=(0,d.useState)(!1),c=l[0],s=l[1];(0,d.useEffect)(function(){o.initialFocus&&i.focusTarget&&(c||(i.focus(i.focusTarget),s(!0)))},[o.initialFocus,c,i.focus,i.focusTarget,i]);var u=[o.classNames.root,o.className];o.numberOfMonths>1&&u.push(o.classNames.multiple_months),o.showWeekNumber&&u.push(o.classNames.with_weeknumber);var f=tu(tu({},o.styles.root),o.style),p=Object.keys(r).filter(function(e){return e.startsWith("data-")}).reduce(function(e,t){var n;return tu(tu({},e),((n={})[t]=r[t],n))},{}),h=null!==(n=null===(t=r.components)||void 0===t?void 0:t.Months)&&void 0!==n?n:nv;return th.jsx("div",tu({className:u.join(" "),style:f,dir:o.dir,id:o.id,nonce:r.nonce,title:r.title,lang:r.lang},p,{children:th.jsx(h,{children:a.displayMonths.map(function(e,t){return th.jsx(ng,{displayIndex:t,displayMonth:e},t)})})}))}function nb(e){var t=e.children,n=function(e,t){var n={};for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&0>t.indexOf(r)&&(n[r]=e[r]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var o=0,r=Object.getOwnPropertySymbols(e);ot.indexOf(r[o])&&Object.prototype.propertyIsEnumerable.call(e,r[o])&&(n[r[o]]=e[r[o]]);return n}(e,["children"]);return th.jsx(tS,{initialProps:n,children:th.jsx(tM,{children:th.jsx(no,{initialProps:n,children:th.jsx(tW,{initialProps:n,children:th.jsx(tG,{initialProps:n,children:th.jsx(t8,{children:th.jsx(nt,{children:t})})})})})})})}function nx(e){return th.jsx(nb,tu({},e,{children:th.jsx(ny,{initialProps:e})}))}let nw=e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M10.8284 12.0007L15.7782 16.9504L14.364 18.3646L8 12.0007L14.364 5.63672L15.7782 7.05093L10.8284 12.0007Z"}))},nS=e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"}))},nk=e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M4.83582 12L11.0429 18.2071L12.4571 16.7929L7.66424 12L12.4571 7.20712L11.0429 5.79291L4.83582 12ZM10.4857 12L16.6928 18.2071L18.107 16.7929L13.3141 12L18.107 7.20712L16.6928 5.79291L10.4857 12Z"}))},nE=e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M19.1642 12L12.9571 5.79291L11.5429 7.20712L16.3358 12L11.5429 16.7929L12.9571 18.2071L19.1642 12ZM13.5143 12L7.30722 5.79291L5.89301 7.20712L10.6859 12L5.89301 16.7929L7.30722 18.2071L13.5143 12Z"}))};var nC=n(84264);n(41649);var nO=n(1526),nj=n(7084),nP=n(26898);let nN={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-1",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-1.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-1.5",fontSize:"text-lg"},xl:{paddingX:"px-3.5",paddingY:"py-1.5",fontSize:"text-xl"}},nM={xs:{paddingX:"px-2",paddingY:"py-0.5",fontSize:"text-xs"},sm:{paddingX:"px-2.5",paddingY:"py-0.5",fontSize:"text-sm"},md:{paddingX:"px-3",paddingY:"py-0.5",fontSize:"text-md"},lg:{paddingX:"px-3.5",paddingY:"py-0.5",fontSize:"text-lg"},xl:{paddingX:"px-4",paddingY:"py-1",fontSize:"text-xl"}},nI={xs:{height:"h-4",width:"w-4"},sm:{height:"h-4",width:"w-4"},md:{height:"h-4",width:"w-4"},lg:{height:"h-5",width:"w-5"},xl:{height:"h-6",width:"w-6"}},nR={[nj.wu.Increase]:{bgColor:(0,eJ.bM)(nj.fr.Emerald,nP.K.background).bgColor,textColor:(0,eJ.bM)(nj.fr.Emerald,nP.K.text).textColor},[nj.wu.ModerateIncrease]:{bgColor:(0,eJ.bM)(nj.fr.Emerald,nP.K.background).bgColor,textColor:(0,eJ.bM)(nj.fr.Emerald,nP.K.text).textColor},[nj.wu.Decrease]:{bgColor:(0,eJ.bM)(nj.fr.Rose,nP.K.background).bgColor,textColor:(0,eJ.bM)(nj.fr.Rose,nP.K.text).textColor},[nj.wu.ModerateDecrease]:{bgColor:(0,eJ.bM)(nj.fr.Rose,nP.K.background).bgColor,textColor:(0,eJ.bM)(nj.fr.Rose,nP.K.text).textColor},[nj.wu.Unchanged]:{bgColor:(0,eJ.bM)(nj.fr.Orange,nP.K.background).bgColor,textColor:(0,eJ.bM)(nj.fr.Orange,nP.K.text).textColor}},nT={[nj.wu.Increase]:e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M13.0001 7.82843V20H11.0001V7.82843L5.63614 13.1924L4.22192 11.7782L12.0001 4L19.7783 11.7782L18.3641 13.1924L13.0001 7.82843Z"}))},[nj.wu.ModerateIncrease]:e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M16.0037 9.41421L7.39712 18.0208L5.98291 16.6066L14.5895 8H7.00373V6H18.0037V17H16.0037V9.41421Z"}))},[nj.wu.Decrease]:e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M13.0001 16.1716L18.3641 10.8076L19.7783 12.2218L12.0001 20L4.22192 12.2218L5.63614 10.8076L11.0001 16.1716V4H13.0001V16.1716Z"}))},[nj.wu.ModerateDecrease]:e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M14.5895 16.0032L5.98291 7.39664L7.39712 5.98242L16.0037 14.589V7.00324H18.0037V18.0032H7.00373V16.0032H14.5895Z"}))},[nj.wu.Unchanged]:e=>{var t=(0,u._T)(e,[]);return d.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),d.createElement("path",{d:"M16.1716 10.9999L10.8076 5.63589L12.2218 4.22168L20 11.9999L12.2218 19.778L10.8076 18.3638L16.1716 12.9999H4V10.9999H16.1716Z"}))}},nA=(0,eJ.fn)("BadgeDelta");d.forwardRef((e,t)=>{let{deltaType:n=nj.wu.Increase,isIncreasePositive:r=!0,size:o=nj.u8.SM,tooltip:i,children:a,className:l}=e,c=(0,u._T)(e,["deltaType","isIncreasePositive","size","tooltip","children","className"]),s=nT[n],f=(0,eJ.Fo)(n,r),p=a?nM:nN,{tooltipProps:h,getReferenceProps:m}=(0,nO.l)();return d.createElement("span",Object.assign({ref:(0,eJ.lq)([t,h.refs.setReference]),className:(0,es.q)(nA("root"),"w-max flex-shrink-0 inline-flex justify-center items-center cursor-default rounded-tremor-full bg-opacity-20 dark:bg-opacity-25",nR[f].bgColor,nR[f].textColor,p[o].paddingX,p[o].paddingY,p[o].fontSize,l)},m,c),d.createElement(nO.Z,Object.assign({text:i},h)),d.createElement(s,{className:(0,es.q)(nA("icon"),"shrink-0",a?(0,es.q)("-ml-1 mr-1.5"):nI[o].height,nI[o].width)}),a?d.createElement("p",{className:(0,es.q)(nA("text"),"text-sm whitespace-nowrap")},a):null)}).displayName="BadgeDelta";var n_=n(47323);let nD=e=>{var{onClick:t,icon:n}=e,r=(0,u._T)(e,["onClick","icon"]);return d.createElement("button",Object.assign({type:"button",className:(0,es.q)("flex items-center justify-center p-1 h-7 w-7 outline-none focus:ring-2 transition duration-100 border border-tremor-border dark:border-dark-tremor-border hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-muted rounded-tremor-small focus:border-tremor-brand-subtle select-none dark:focus:border-dark-tremor-brand-subtle focus:ring-tremor-brand-muted dark:focus:ring-dark-tremor-brand-muted text-tremor-content-subtle dark:text-dark-tremor-content-subtle hover:text-tremor-content dark:hover:text-dark-tremor-content")},r),d.createElement(n_.Z,{onClick:t,icon:n,variant:"simple",color:"slate",size:"sm"}))};function nZ(e){var{mode:t,defaultMonth:n,selected:r,onSelect:o,locale:i,disabled:a,enableYearNavigation:l,classNames:c,weekStartsOn:s=0}=e,f=(0,u._T)(e,["mode","defaultMonth","selected","onSelect","locale","disabled","enableYearNavigation","classNames","weekStartsOn"]);return d.createElement(nx,Object.assign({showOutsideDays:!0,mode:t,defaultMonth:n,selected:r,onSelect:o,locale:i,disabled:a,weekStartsOn:s,classNames:Object.assign({months:"flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",month:"space-y-4",caption:"flex justify-center pt-2 relative items-center",caption_label:"text-tremor-default text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis font-medium",nav:"space-x-1 flex items-center",nav_button:"flex items-center justify-center p-1 h-7 w-7 outline-none focus:ring-2 transition duration-100 border border-tremor-border dark:border-dark-tremor-border hover:bg-tremor-background-muted dark:hover:bg-dark-tremor-background-muted rounded-tremor-small focus:border-tremor-brand-subtle dark:focus:border-dark-tremor-brand-subtle focus:ring-tremor-brand-muted dark:focus:ring-dark-tremor-brand-muted text-tremor-content-subtle dark:text-dark-tremor-content-subtle hover:text-tremor-content dark:hover:text-dark-tremor-content",nav_button_previous:"absolute left-1",nav_button_next:"absolute right-1",table:"w-full border-collapse space-y-1",head_row:"flex",head_cell:"w-9 font-normal text-center text-tremor-content-subtle dark:text-dark-tremor-content-subtle",row:"flex w-full mt-0.5",cell:"text-center p-0 relative focus-within:relative text-tremor-default text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis",day:"h-9 w-9 p-0 hover:bg-tremor-background-subtle dark:hover:bg-dark-tremor-background-subtle outline-tremor-brand dark:outline-dark-tremor-brand rounded-tremor-default",day_today:"font-bold",day_selected:"aria-selected:bg-tremor-background-emphasis aria-selected:text-tremor-content-inverted dark:aria-selected:bg-dark-tremor-background-emphasis dark:aria-selected:text-dark-tremor-content-inverted ",day_disabled:"text-tremor-content-subtle dark:text-dark-tremor-content-subtle disabled:hover:bg-transparent",day_outside:"text-tremor-content-subtle dark:text-dark-tremor-content-subtle"},c),components:{IconLeft:e=>{var t=(0,u._T)(e,[]);return d.createElement(nw,Object.assign({className:"h-4 w-4"},t))},IconRight:e=>{var t=(0,u._T)(e,[]);return d.createElement(nS,Object.assign({className:"h-4 w-4"},t))},Caption:e=>{var t=(0,u._T)(e,[]);let{goToMonth:n,nextMonth:r,previousMonth:o,currentMonth:a}=tI();return d.createElement("div",{className:"flex justify-between items-center"},d.createElement("div",{className:"flex items-center space-x-1"},l&&d.createElement(nD,{onClick:()=>a&&n(tl(a,-1)),icon:nk}),d.createElement(nD,{onClick:()=>o&&n(o),icon:nw})),d.createElement(nC.Z,{className:"text-tremor-default tabular-nums capitalize text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis font-medium"},eQ(t.displayMonth,"LLLL yyy",{locale:i})),d.createElement("div",{className:"flex items-center space-x-1"},d.createElement(nD,{onClick:()=>r&&n(r),icon:nS}),l&&d.createElement(nD,{onClick:()=>a&&n(tl(a,1)),icon:nE})))}}},f))}nZ.displayName="DateRangePicker",n(27281);var nL=n(57365),nz=n(44140);let nB=el(),nF=d.forwardRef((e,t)=>{var n,r;let{value:o,defaultValue:i,onValueChange:a,enableSelect:l=!0,minDate:c,maxDate:s,placeholder:f="Select range",selectPlaceholder:p="Select range",disabled:h=!1,locale:m=eV,enableClear:g=!0,displayFormat:v,children:y,className:b,enableYearNavigation:x=!1,weekStartsOn:w=0,disabledDates:S}=e,k=(0,u._T)(e,["value","defaultValue","onValueChange","enableSelect","minDate","maxDate","placeholder","selectPlaceholder","disabled","locale","enableClear","displayFormat","children","className","enableYearNavigation","weekStartsOn","disabledDates"]),[E,C]=(0,nz.Z)(i,o),[O,j]=(0,d.useState)(!1),[P,N]=(0,d.useState)(!1),M=(0,d.useMemo)(()=>{let e=[];return c&&e.push({before:c}),s&&e.push({after:s}),[...e,...null!=S?S:[]]},[c,s,S]),I=(0,d.useMemo)(()=>{let e=new Map;return y?d.Children.forEach(y,t=>{var n;e.set(t.props.value,{text:null!==(n=(0,eu.qg)(t))&&void 0!==n?n:t.props.value,from:t.props.from,to:t.props.to})}):e6.forEach(t=>{e.set(t.value,{text:t.text,from:t.from,to:nB})}),e},[y]),R=(0,d.useMemo)(()=>{if(y)return(0,eu.sl)(y);let e=new Map;return e6.forEach(t=>e.set(t.value,t.text)),e},[y]),T=(null==E?void 0:E.selectValue)||"",A=e1(null==E?void 0:E.from,c,T,I),_=e2(null==E?void 0:E.to,s,T,I),D=A||_?e4(A,_,m,v):f,Z=ec(null!==(r=null!==(n=null!=_?_:A)&&void 0!==n?n:s)&&void 0!==r?r:nB),L=g&&!h;return d.createElement("div",Object.assign({ref:t,className:(0,es.q)("w-full min-w-[10rem] relative flex justify-between text-tremor-default max-w-sm shadow-tremor-input dark:shadow-dark-tremor-input rounded-tremor-default",b)},k),d.createElement(J,{as:"div",className:(0,es.q)("w-full",l?"rounded-l-tremor-default":"rounded-tremor-default",O&&"ring-2 ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted z-10")},d.createElement("div",{className:"relative w-full"},d.createElement(J.Button,{onFocus:()=>j(!0),onBlur:()=>j(!1),disabled:h,className:(0,es.q)("w-full outline-none text-left whitespace-nowrap truncate focus:ring-2 transition duration-100 rounded-l-tremor-default flex flex-nowrap border pl-3 py-2","rounded-l-tremor-default border-tremor-border text-tremor-content-emphasis focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:text-dark-tremor-content-emphasis dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",l?"rounded-l-tremor-default":"rounded-tremor-default",L?"pr-8":"pr-4",(0,eu.um)((0,eu.Uh)(A||_),h))},d.createElement(en,{className:(0,es.q)(e0("calendarIcon"),"flex-none shrink-0 h-5 w-5 -ml-0.5 mr-2","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle"),"aria-hidden":"true"}),d.createElement("p",{className:"truncate"},D)),L&&A?d.createElement("button",{type:"button",className:(0,es.q)("absolute outline-none inset-y-0 right-0 flex items-center transition duration-100 mr-4"),onClick:e=>{e.preventDefault(),null==a||a({}),C({})}},d.createElement(er.Z,{className:(0,es.q)(e0("clearIcon"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null),d.createElement(ee.u,{className:"absolute z-10 min-w-min left-0",enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},d.createElement(J.Panel,{focus:!0,className:(0,es.q)("divide-y overflow-y-auto outline-none rounded-tremor-default p-3 border my-1","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},d.createElement(nZ,Object.assign({mode:"range",showOutsideDays:!0,defaultMonth:Z,selected:{from:A,to:_},onSelect:e=>{null==a||a({from:null==e?void 0:e.from,to:null==e?void 0:e.to}),C({from:null==e?void 0:e.from,to:null==e?void 0:e.to})},locale:m,disabled:M,enableYearNavigation:x,classNames:{day_range_middle:(0,es.q)("!rounded-none aria-selected:!bg-tremor-background-subtle aria-selected:dark:!bg-dark-tremor-background-subtle aria-selected:!text-tremor-content aria-selected:dark:!bg-dark-tremor-background-subtle"),day_range_start:"rounded-r-none rounded-l-tremor-small aria-selected:text-tremor-brand-inverted dark:aria-selected:text-dark-tremor-brand-inverted",day_range_end:"rounded-l-none rounded-r-tremor-small aria-selected:text-tremor-brand-inverted dark:aria-selected:text-dark-tremor-brand-inverted"},weekStartsOn:w},e))))),l&&d.createElement(et.R,{as:"div",className:(0,es.q)("w-48 -ml-px rounded-r-tremor-default",P&&"ring-2 ring-tremor-brand-muted dark:ring-dark-tremor-brand-muted z-10"),value:T,onChange:e=>{let{from:t,to:n}=I.get(e),r=null!=n?n:nB;null==a||a({from:t,to:r,selectValue:e}),C({from:t,to:r,selectValue:e})},disabled:h},e=>{var t;let{value:n}=e;return d.createElement(d.Fragment,null,d.createElement(et.R.Button,{onFocus:()=>N(!0),onBlur:()=>N(!1),className:(0,es.q)("w-full outline-none text-left whitespace-nowrap truncate rounded-r-tremor-default transition duration-100 border px-4 py-2","border-tremor-border shadow-tremor-input text-tremor-content-emphasis focus:border-tremor-brand-subtle","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:text-dark-tremor-content-emphasis dark:focus:border-dark-tremor-brand-subtle",(0,eu.um)((0,eu.Uh)(n),h))},n&&null!==(t=R.get(n))&&void 0!==t?t:p),d.createElement(ee.u,{className:"absolute z-10 w-full inset-x-0 right-0",enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},d.createElement(et.R.Options,{className:(0,es.q)("divide-y overflow-y-auto outline-none border my-1","shadow-tremor-dropdown bg-tremor-background border-tremor-border divide-tremor-border rounded-tremor-default","dark:shadow-dark-tremor-dropdown dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border")},null!=y?y:e6.map(e=>d.createElement(nL.Z,{key:e.value,value:e.value},e.text)))))}))});nF.displayName="DateRangePicker"},92414:function(e,t,n){"use strict";n.d(t,{Z:function(){return v}});var r=n(5853),o=n(2265);n(42698),n(64016),n(8710);var i=n(33232),a=n(44140),l=n(58747);let c=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"currentColor"},t),o.createElement("path",{d:"M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"}))};var s=n(4537),u=n(28517),d=n(33044);let f=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({xmlns:"http://www.w3.org/2000/svg",width:"100%",height:"100%",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},t),o.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),o.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"}))};var p=n(65954),h=n(1153),m=n(96398);let g=(0,h.fn)("MultiSelect"),v=o.forwardRef((e,t)=>{let{defaultValue:n,value:h,onValueChange:v,placeholder:y="Select...",placeholderSearch:b="Search",disabled:x=!1,icon:w,children:S,className:k}=e,E=(0,r._T)(e,["defaultValue","value","onValueChange","placeholder","placeholderSearch","disabled","icon","children","className"]),[C,O]=(0,a.Z)(n,h),{reactElementChildren:j,optionsAvailable:P}=(0,o.useMemo)(()=>{let e=o.Children.toArray(S).filter(o.isValidElement);return{reactElementChildren:e,optionsAvailable:(0,m.n0)("",e)}},[S]),[N,M]=(0,o.useState)(""),I=(null!=C?C:[]).length>0,R=(0,o.useMemo)(()=>N?(0,m.n0)(N,j):P,[N,j,P]),T=()=>{M("")};return o.createElement(u.R,Object.assign({as:"div",ref:t,defaultValue:C,value:C,onChange:e=>{null==v||v(e),O(e)},disabled:x,className:(0,p.q)("w-full min-w-[10rem] relative text-tremor-default",k)},E,{multiple:!0}),e=>{let{value:t}=e;return o.createElement(o.Fragment,null,o.createElement(u.R.Button,{className:(0,p.q)("w-full outline-none text-left whitespace-nowrap truncate rounded-tremor-default focus:ring-2 transition duration-100 border pr-8 py-1.5","border-tremor-border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",w?"pl-11 -ml-0.5":"pl-3",(0,m.um)(t.length>0,x))},w&&o.createElement("span",{className:(0,p.q)("absolute inset-y-0 left-0 flex items-center ml-px pl-2.5")},o.createElement(w,{className:(0,p.q)(g("Icon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),o.createElement("div",{className:"h-6 flex items-center"},t.length>0?o.createElement("div",{className:"flex flex-nowrap overflow-x-scroll [&::-webkit-scrollbar]:hidden [scrollbar-width:none] gap-x-1 mr-5 -ml-1.5 relative"},P.filter(e=>t.includes(e.props.value)).map((e,n)=>{var r;return o.createElement("div",{key:n,className:(0,p.q)("max-w-[100px] lg:max-w-[200px] flex justify-center items-center pl-2 pr-1.5 py-1 font-medium","rounded-tremor-small","bg-tremor-background-muted dark:bg-dark-tremor-background-muted","bg-tremor-background-subtle dark:bg-dark-tremor-background-subtle","text-tremor-content-default dark:text-dark-tremor-content-default","text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis")},o.createElement("div",{className:"text-xs truncate "},null!==(r=e.props.children)&&void 0!==r?r:e.props.value),o.createElement("div",{onClick:n=>{n.preventDefault();let r=t.filter(t=>t!==e.props.value);null==v||v(r),O(r)}},o.createElement(f,{className:(0,p.q)(g("clearIconItem"),"cursor-pointer rounded-tremor-full w-3.5 h-3.5 ml-2","text-tremor-content-subtle hover:text-tremor-content","dark:text-dark-tremor-content-subtle dark:hover:text-tremor-content")})))})):o.createElement("span",null,y)),o.createElement("span",{className:(0,p.q)("absolute inset-y-0 right-0 flex items-center mr-2.5")},o.createElement(l.Z,{className:(0,p.q)(g("arrowDownIcon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}))),I&&!x?o.createElement("button",{type:"button",className:(0,p.q)("absolute inset-y-0 right-0 flex items-center mr-8"),onClick:e=>{e.preventDefault(),O([]),null==v||v([])}},o.createElement(s.Z,{className:(0,p.q)(g("clearIconAllItems"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null,o.createElement(d.u,{className:"absolute z-10 w-full",enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},o.createElement(u.R.Options,{className:(0,p.q)("divide-y overflow-y-auto outline-none rounded-tremor-default max-h-[228px] left-0 border my-1","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},o.createElement("div",{className:(0,p.q)("flex items-center w-full px-2.5","bg-tremor-background-muted","dark:bg-dark-tremor-background-muted")},o.createElement("span",null,o.createElement(c,{className:(0,p.q)("flex-none w-4 h-4 mr-2","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),o.createElement("input",{name:"search",type:"input",autoComplete:"off",placeholder:b,className:(0,p.q)("w-full focus:outline-none focus:ring-none bg-transparent text-tremor-default py-2","text-tremor-content-emphasis","dark:text-dark-tremor-content-emphasis"),onKeyDown:e=>{"Space"===e.code&&""!==e.target.value&&e.stopPropagation()},onChange:e=>M(e.target.value),value:N})),o.createElement(i.Z.Provider,Object.assign({},{onBlur:{handleResetSearch:T}},{value:{selectedValue:t}}),R))))})});v.displayName="MultiSelect"},46030:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=n(5853);n(42698),n(64016),n(8710);var o=n(33232),i=n(2265),a=n(65954),l=n(1153),c=n(28517);let s=(0,l.fn)("MultiSelectItem"),u=i.forwardRef((e,t)=>{let{value:n,className:u,children:d}=e,f=(0,r._T)(e,["value","className","children"]),{selectedValue:p}=(0,i.useContext)(o.Z),h=(0,l.NZ)(n,p);return i.createElement(c.R.Option,Object.assign({className:(0,a.q)(s("root"),"flex justify-start items-center cursor-default text-tremor-default p-2.5","ui-active:bg-tremor-background-muted ui-active:text-tremor-content-strong ui-selected:text-tremor-content-strong text-tremor-content-emphasis","dark:ui-active:bg-dark-tremor-background-muted dark:ui-active:text-dark-tremor-content-strong dark:ui-selected:text-dark-tremor-content-strong dark:ui-selected:bg-dark-tremor-background-muted dark:text-dark-tremor-content-emphasis",u),ref:t,key:n,value:n},f),i.createElement("input",{type:"checkbox",className:(0,a.q)(s("checkbox"),"flex-none focus:ring-none focus:outline-none cursor-pointer mr-2.5","accent-tremor-brand","dark:accent-dark-tremor-brand"),checked:h,readOnly:!0}),i.createElement("span",{className:"whitespace-nowrap truncate"},null!=d?d:n))});u.displayName="MultiSelectItem"},30150:function(e,t,n){"use strict";n.d(t,{Z:function(){return f}});var r=n(5853),o=n(2265);let i=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),o.createElement("path",{d:"M12 4v16m8-8H4"}))},a=e=>{var t=(0,r._T)(e,[]);return o.createElement("svg",Object.assign({},t,{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"2.5"}),o.createElement("path",{d:"M20 12H4"}))};var l=n(65954),c=n(1153),s=n(69262);let u="flex mx-auto text-tremor-content-subtle dark:text-dark-tremor-content-subtle",d="cursor-pointer hover:text-tremor-content dark:hover:text-dark-tremor-content",f=o.forwardRef((e,t)=>{let{onSubmit:n,enableStepper:f=!0,disabled:p,onValueChange:h,onChange:m}=e,g=(0,r._T)(e,["onSubmit","enableStepper","disabled","onValueChange","onChange"]),v=(0,o.useRef)(null),[y,b]=o.useState(!1),x=o.useCallback(()=>{b(!0)},[]),w=o.useCallback(()=>{b(!1)},[]),[S,k]=o.useState(!1),E=o.useCallback(()=>{k(!0)},[]),C=o.useCallback(()=>{k(!1)},[]);return o.createElement(s.Z,Object.assign({type:"number",ref:(0,c.lq)([v,t]),disabled:p,makeInputClassName:(0,c.fn)("NumberInput"),onKeyDown:e=>{var t;if("Enter"===e.key&&!e.ctrlKey&&!e.altKey&&!e.shiftKey){let e=null===(t=v.current)||void 0===t?void 0:t.value;null==n||n(parseFloat(null!=e?e:""))}"ArrowDown"===e.key&&x(),"ArrowUp"===e.key&&E()},onKeyUp:e=>{"ArrowDown"===e.key&&w(),"ArrowUp"===e.key&&C()},onChange:e=>{p||(null==h||h(parseFloat(e.target.value)),null==m||m(e))},stepper:f?o.createElement("div",{className:(0,l.q)("flex justify-center align-middle")},o.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null===(e=v.current)||void 0===e||e.stepDown(),null===(t=v.current)||void 0===t||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,l.q)(!p&&d,u,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},o.createElement(a,{"data-testid":"step-down",className:(y?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"})),o.createElement("div",{tabIndex:-1,onClick:e=>e.preventDefault(),onMouseDown:e=>e.preventDefault(),onTouchStart:e=>{e.cancelable&&e.preventDefault()},onMouseUp:()=>{var e,t;p||(null===(e=v.current)||void 0===e||e.stepUp(),null===(t=v.current)||void 0===t||t.dispatchEvent(new Event("input",{bubbles:!0})))},className:(0,l.q)(!p&&d,u,"group py-[10px] px-2.5 border-l border-tremor-border dark:border-dark-tremor-border")},o.createElement(i,{"data-testid":"step-up",className:(S?"scale-95":"")+" h-4 w-4 duration-75 transition group-active:scale-95"}))):null},g))});f.displayName="NumberInput"},27281:function(e,t,n){"use strict";n.d(t,{Z:function(){return h}});var r=n(5853),o=n(2265),i=n(58747),a=n(4537),l=n(65954),c=n(1153),s=n(96398),u=n(28517),d=n(33044),f=n(44140);let p=(0,c.fn)("Select"),h=o.forwardRef((e,t)=>{let{defaultValue:n,value:c,onValueChange:h,placeholder:m="Select...",disabled:g=!1,icon:v,enableClear:y=!0,children:b,className:x}=e,w=(0,r._T)(e,["defaultValue","value","onValueChange","placeholder","disabled","icon","enableClear","children","className"]),[S,k]=(0,f.Z)(n,c),E=(0,o.useMemo)(()=>{let e=o.Children.toArray(b).filter(o.isValidElement);return(0,s.sl)(e)},[b]);return o.createElement(u.R,Object.assign({as:"div",ref:t,defaultValue:S,value:S,onChange:e=>{null==h||h(e),k(e)},disabled:g,className:(0,l.q)("w-full min-w-[10rem] relative text-tremor-default",x)},w),e=>{var t;let{value:n}=e;return o.createElement(o.Fragment,null,o.createElement(u.R.Button,{className:(0,l.q)("w-full outline-none text-left whitespace-nowrap truncate rounded-tremor-default focus:ring-2 transition duration-100 border pr-8 py-2","border-tremor-border shadow-tremor-input focus:border-tremor-brand-subtle focus:ring-tremor-brand-muted","dark:border-dark-tremor-border dark:shadow-dark-tremor-input dark:focus:border-dark-tremor-brand-subtle dark:focus:ring-dark-tremor-brand-muted",v?"pl-10":"pl-3",(0,s.um)((0,s.Uh)(n),g))},v&&o.createElement("span",{className:(0,l.q)("absolute inset-y-0 left-0 flex items-center ml-px pl-2.5")},o.createElement(v,{className:(0,l.q)(p("Icon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})),o.createElement("span",{className:"w-[90%] block truncate"},n&&null!==(t=E.get(n))&&void 0!==t?t:m),o.createElement("span",{className:(0,l.q)("absolute inset-y-0 right-0 flex items-center mr-3")},o.createElement(i.Z,{className:(0,l.q)(p("arrowDownIcon"),"flex-none h-5 w-5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}))),y&&S?o.createElement("button",{type:"button",className:(0,l.q)("absolute inset-y-0 right-0 flex items-center mr-8"),onClick:e=>{e.preventDefault(),k(""),null==h||h("")}},o.createElement(a.Z,{className:(0,l.q)(p("clearIcon"),"flex-none h-4 w-4","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")})):null,o.createElement(d.u,{className:"absolute z-10 w-full",enter:"transition ease duration-100 transform",enterFrom:"opacity-0 -translate-y-4",enterTo:"opacity-100 translate-y-0",leave:"transition ease duration-100 transform",leaveFrom:"opacity-100 translate-y-0",leaveTo:"opacity-0 -translate-y-4"},o.createElement(u.R.Options,{className:(0,l.q)("divide-y overflow-y-auto outline-none rounded-tremor-default max-h-[228px] left-0 border my-1","bg-tremor-background border-tremor-border divide-tremor-border shadow-tremor-dropdown","dark:bg-dark-tremor-background dark:border-dark-tremor-border dark:divide-dark-tremor-border dark:shadow-dark-tremor-dropdown")},b)))})});h.displayName="Select"},57365:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=n(5853),o=n(2265),i=n(28517),a=n(65954);let l=(0,n(1153).fn)("SelectItem"),c=o.forwardRef((e,t)=>{let{value:n,icon:c,className:s,children:u}=e,d=(0,r._T)(e,["value","icon","className","children"]);return o.createElement(i.R.Option,Object.assign({className:(0,a.q)(l("root"),"flex justify-start items-center cursor-default text-tremor-default px-2.5 py-2.5","ui-active:bg-tremor-background-muted ui-active:text-tremor-content-strong ui-selected:text-tremor-content-strong ui-selected:bg-tremor-background-muted text-tremor-content-emphasis","dark:ui-active:bg-dark-tremor-background-muted dark:ui-active:text-dark-tremor-content-strong dark:ui-selected:text-dark-tremor-content-strong dark:ui-selected:bg-dark-tremor-background-muted dark:text-dark-tremor-content-emphasis",s),ref:t,key:n,value:n},d),c&&o.createElement(c,{className:(0,a.q)(l("icon"),"flex-none w-5 h-5 mr-1.5","text-tremor-content-subtle","dark:text-dark-tremor-content-subtle")}),o.createElement("span",{className:"whitespace-nowrap truncate"},null!=u?u:n))});c.displayName="SelectItem"},92858:function(e,t,n){"use strict";n.d(t,{Z:function(){return M}});var r=n(5853),o=n(2265),i=n(62963),a=n(90945),l=n(13323),c=n(17684),s=n(80004),u=n(93689),d=n(38198),f=n(47634),p=n(56314),h=n(27847),m=n(64518);let g=(0,o.createContext)(null),v=Object.assign((0,h.yV)(function(e,t){let n=(0,c.M)(),{id:r="headlessui-description-".concat(n),...i}=e,a=function e(){let t=(0,o.useContext)(g);if(null===t){let t=Error("You used a component, but it is not inside a relevant parent.");throw Error.captureStackTrace&&Error.captureStackTrace(t,e),t}return t}(),l=(0,u.T)(t);(0,m.e)(()=>a.register(r),[r,a.register]);let s={ref:l,...a.props,id:r};return(0,h.sY)({ourProps:s,theirProps:i,slot:a.slot||{},defaultTag:"p",name:a.name||"Description"})}),{});var y=n(37388);let b=(0,o.createContext)(null),x=Object.assign((0,h.yV)(function(e,t){let n=(0,c.M)(),{id:r="headlessui-label-".concat(n),passive:i=!1,...a}=e,l=function e(){let t=(0,o.useContext)(b);if(null===t){let t=Error("You used a