diff --git a/.circleci/config.yml b/.circleci/config.yml
index 52c2115bf5f..92a26af32fc 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -950,14 +950,14 @@ jobs:
command: |
pwd
ls
- python -m pytest -vv tests/test_litellm --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit-litellm.xml --durations=10 -n 4
+ python -m pytest -vv tests/test_litellm --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit-litellm.xml --durations=10 -n 8
no_output_timeout: 120m
- run:
name: Run enterprise tests
command: |
pwd
ls
- python -m pytest -vv tests/enterprise --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit-enterprise.xml --durations=10 -n 4
+ python -m pytest -vv tests/enterprise --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit-enterprise.xml --durations=10 -n 8
no_output_timeout: 120m
- run:
name: Rename the coverage files
@@ -1358,6 +1358,7 @@ jobs:
# - run: python ./tests/documentation_tests/test_general_setting_keys.py
- run: python ./tests/code_coverage_tests/check_licenses.py
- run: python ./tests/code_coverage_tests/router_code_coverage.py
+ - run: python ./tests/code_coverage_tests/code_qa_check_tests.py
- run: python ./tests/code_coverage_tests/test_proxy_types_import.py
- run: python ./tests/code_coverage_tests/callback_manager_test.py
- run: python ./tests/code_coverage_tests/recursive_detector.py
diff --git a/.github/workflows/test-litellm.yml b/.github/workflows/test-litellm.yml
index 66471e07320..3a61728305f 100644
--- a/.github/workflows/test-litellm.yml
+++ b/.github/workflows/test-litellm.yml
@@ -30,6 +30,7 @@ jobs:
poetry install --with dev,proxy-dev --extras proxy
poetry run pip install "pytest-retry==1.6.3"
poetry run pip install pytest-xdist
+ poetry run pip install "google-genai==1.22.0"
- name: Setup litellm-enterprise as local package
run: |
cd enterprise
diff --git a/README.md b/README.md
index eeca3518021..47878747a60 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ messages = [{ "content": "Hello, how are you?","role": "user"}]
response = completion(model="openai/gpt-4o", messages=messages)
# anthropic call
-response = completion(model="anthropic/claude-3-sonnet-20240229", messages=messages)
+response = completion(model="anthropic/claude-sonnet-4-20250514", messages=messages)
print(response)
```
@@ -80,9 +80,9 @@ print(response)
```json
{
- "id": "chatcmpl-565d891b-a42e-4c39-8d14-82a1f5208885",
- "created": 1734366691,
- "model": "claude-3-sonnet-20240229",
+ "id": "chatcmpl-1214900a-6cdd-4148-b663-b5e2f642b4de",
+ "created": 1751494488,
+ "model": "claude-sonnet-4-20250514",
"object": "chat.completion",
"system_fingerprint": null,
"choices": [
@@ -90,7 +90,7 @@ print(response)
"finish_reason": "stop",
"index": 0,
"message": {
- "content": "Hello! As an AI language model, I don't have feelings, but I'm operating properly and ready to assist you with any questions or tasks you may have. How can I help you today?",
+ "content": "Hello! I'm doing well, thank you for asking. I'm here and ready to help with whatever you'd like to discuss or work on. How are you doing today?",
"role": "assistant",
"tool_calls": null,
"function_call": null
@@ -98,9 +98,9 @@ print(response)
}
],
"usage": {
- "completion_tokens": 43,
+ "completion_tokens": 39,
"prompt_tokens": 13,
- "total_tokens": 56,
+ "total_tokens": 52,
"completion_tokens_details": null,
"prompt_tokens_details": {
"audio_tokens": null,
@@ -141,8 +141,8 @@ response = completion(model="openai/gpt-4o", messages=messages, stream=True)
for part in response:
print(part.choices[0].delta.content or "")
-# claude 2
-response = completion('anthropic/claude-3-sonnet-20240229', messages, stream=True)
+# claude sonnet 4
+response = completion('anthropic/claude-sonnet-4-20250514', messages, stream=True)
for part in response:
print(part)
```
@@ -151,9 +151,9 @@ for part in response:
```json
{
- "id": "chatcmpl-2be06597-eb60-4c70-9ec5-8cd2ab1b4697",
- "created": 1734366925,
- "model": "claude-3-sonnet-20240229",
+ "id": "chatcmpl-fe575c37-5004-4926-ae5e-bfbc31f356ca",
+ "created": 1751494808,
+ "model": "claude-sonnet-4-20250514",
"object": "chat.completion.chunk",
"system_fingerprint": null,
"choices": [
@@ -161,6 +161,7 @@ for part in response:
"finish_reason": null,
"index": 0,
"delta": {
+ "provider_specific_fields": null,
"content": "Hello",
"role": "assistant",
"function_call": null,
@@ -169,7 +170,10 @@ for part in response:
},
"logprobs": null
}
- ]
+ ],
+ "provider_specific_fields": null,
+ "stream_options": null,
+ "citations": null
}
```
diff --git a/docs/my-website/docs/assistants.md b/docs/my-website/docs/assistants.md
index 4032c74557f..d262b492a70 100644
--- a/docs/my-website/docs/assistants.md
+++ b/docs/my-website/docs/assistants.md
@@ -279,7 +279,7 @@ with run as run:
curl -X POST 'http://0.0.0.0:4000/threads/{thread_id}/runs' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
--D '{
+-d '{
"assistant_id": "asst_6xVZQFFy1Kw87NbnYeNebxTf",
"stream": true
}'
diff --git a/docs/my-website/docs/generateContent.md b/docs/my-website/docs/generateContent.md
new file mode 100644
index 00000000000..e6823ebf05d
--- /dev/null
+++ b/docs/my-website/docs/generateContent.md
@@ -0,0 +1,236 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Google AI generateContent
+
+Use LiteLLM to call Google AI's generateContent endpoints for text generation, multimodal interactions, and streaming responses.
+
+## Overview
+
+| Feature | Supported | Notes |
+|-------|-------|-------|
+| Cost Tracking | ✅ | |
+| Logging | ✅ | works across all integrations |
+| End-user Tracking | ✅ | |
+| Streaming | ✅ | |
+| Fallbacks | ✅ | between supported models |
+| Loadbalancing | ✅ | between supported models |
+
+## Usage
+---
+
+### LiteLLM Python SDK
+
+
+
+
+#### Non-streaming example
+```python showLineNumbers title="Basic Text Generation"
+from litellm.google_genai import agenerate_content
+from google.genai.types import ContentDict, PartDict
+import os
+
+# Set API key
+os.environ["GEMINI_API_KEY"] = "your-gemini-api-key"
+
+contents = ContentDict(
+ parts=[
+ PartDict(text="Hello, can you tell me a short joke?")
+ ],
+ role="user",
+)
+
+response = await agenerate_content(
+ contents=contents,
+ model="gemini/gemini-2.0-flash",
+ max_tokens=100,
+)
+print(response)
+```
+
+#### Streaming example
+```python showLineNumbers title="Streaming Text Generation"
+from litellm.google_genai import agenerate_content_stream
+from google.genai.types import ContentDict, PartDict
+import os
+
+# Set API key
+os.environ["GEMINI_API_KEY"] = "your-gemini-api-key"
+
+contents = ContentDict(
+ parts=[
+ PartDict(text="Write a long story about space exploration")
+ ],
+ role="user",
+)
+
+response = await agenerate_content_stream(
+ contents=contents,
+ model="gemini/gemini-2.0-flash",
+ max_tokens=500,
+)
+
+async for chunk in response:
+ print(chunk)
+```
+
+
+
+
+
+#### Sync non-streaming example
+```python showLineNumbers title="Sync Text Generation"
+from litellm.google_genai import generate_content
+from google.genai.types import ContentDict, PartDict
+import os
+
+# Set API key
+os.environ["GEMINI_API_KEY"] = "your-gemini-api-key"
+
+contents = ContentDict(
+ parts=[
+ PartDict(text="Hello, can you tell me a short joke?")
+ ],
+ role="user",
+)
+
+response = generate_content(
+ contents=contents,
+ model="gemini/gemini-2.0-flash",
+ max_tokens=100,
+)
+print(response)
+```
+
+#### Sync streaming example
+```python showLineNumbers title="Sync Streaming Text Generation"
+from litellm.google_genai import generate_content_stream
+from google.genai.types import ContentDict, PartDict
+import os
+
+# Set API key
+os.environ["GEMINI_API_KEY"] = "your-gemini-api-key"
+
+contents = ContentDict(
+ parts=[
+ PartDict(text="Write a long story about space exploration")
+ ],
+ role="user",
+)
+
+response = generate_content_stream(
+ contents=contents,
+ model="gemini/gemini-2.0-flash",
+ max_tokens=500,
+)
+
+for chunk in response:
+ print(chunk)
+```
+
+
+
+
+### LiteLLM Proxy Server
+
+1. Setup config.yaml
+
+```yaml
+model_list:
+ - model_name: gemini-flash
+ litellm_params:
+ model: gemini/gemini-2.0-flash
+ api_key: os.environ/GEMINI_API_KEY
+```
+
+2. Start proxy
+
+```bash
+litellm --config /path/to/config.yaml
+```
+
+3. Test it!
+
+
+
+
+```python showLineNumbers title="Google GenAI SDK with LiteLLM Proxy"
+from google.genai import Client
+import os
+
+# Configure Google GenAI SDK to use LiteLLM proxy
+os.environ["GOOGLE_GEMINI_BASE_URL"] = "http://localhost:4000"
+os.environ["GEMINI_API_KEY"] = "sk-1234"
+
+client = Client()
+
+response = client.models.generate_content(
+ model="gemini-flash",
+ contents=[
+ {
+ "parts": [{"text": "Write a short story about AI"}],
+ "role": "user"
+ }
+ ],
+ config={"max_output_tokens": 100}
+)
+```
+
+
+
+
+
+
+#### Generate Content
+
+```bash showLineNumbers title="generateContent via LiteLLM Proxy"
+curl -L -X POST 'http://localhost:4000/v1beta/models/gemini-flash:generateContent' \
+-H 'content-type: application/json' \
+-H 'authorization: Bearer sk-1234' \
+-d '{
+ "contents": [
+ {
+ "parts": [
+ {
+ "text": "Write a short story about AI"
+ }
+ ],
+ "role": "user"
+ }
+ ],
+ "generationConfig": {
+ "maxOutputTokens": 100
+ }
+}'
+```
+
+#### Stream Generate Content
+
+```bash showLineNumbers title="streamGenerateContent via LiteLLM Proxy"
+curl -L -X POST 'http://localhost:4000/v1beta/models/gemini-flash:streamGenerateContent' \
+-H 'content-type: application/json' \
+-H 'authorization: Bearer sk-1234' \
+-d '{
+ "contents": [
+ {
+ "parts": [
+ {
+ "text": "Write a long story about space exploration"
+ }
+ ],
+ "role": "user"
+ }
+ ],
+ "generationConfig": {
+ "maxOutputTokens": 500
+ }
+}'
+```
+
+
+
+
+
+## Related
+
+- [Use LiteLLM with gemini-cli](../docs/tutorials/litellm_gemini_cli)
\ No newline at end of file
diff --git a/docs/my-website/docs/guides/security_settings.md b/docs/my-website/docs/guides/security_settings.md
index 008e620c515..7995f6c3c9c 100644
--- a/docs/my-website/docs/guides/security_settings.md
+++ b/docs/my-website/docs/guides/security_settings.md
@@ -3,12 +3,43 @@ import TabItem from '@theme/TabItem';
# SSL, HTTP Proxy Security Settings
-If you're in an environment using an older TTS bundle, with an older encryption, follow this guide.
+If you're in an environment using an older TTS bundle, with an older encryption, follow this guide. By default
+LiteLLM uses the certifi CA bundle for SSL verification, which is compatible with most modern servers.
+ However, if you need to disable SSL verification or use a custom CA bundle, you can do so by following the steps below.
+Be aware that environmental variables take precedence over the settings in the SDK.
-LiteLLM uses HTTPX for network requests, unless otherwise specified.
+LiteLLM uses HTTPX for network requests, unless otherwise specified.
-## 1. Disable SSL verification
+## 1. Custom CA Bundle
+
+You can set a custom CA bundle file path using the `SSL_CERT_FILE` environmental variable or passing a string to the the ssl_verify setting.
+
+
+
+
+```python
+import litellm
+litellm.ssl_verify = "client.pem"
+```
+
+
+
+```yaml
+litellm_settings:
+ ssl_verify: "client.pem"
+```
+
+
+
+
+```bash
+export SSL_CERT_FILE="client.pem"
+```
+
+
+
+## 2. Disable SSL verification
@@ -35,14 +66,42 @@ export SSL_VERIFY="False"
-## 2. Lower security settings
+## 3. Lower security settings
+
+The `ssl_security_level` allows setting a lower security level for SSL connections.
+
+
+
+
+```python
+import litellm
+litellm.ssl_security_level = "DEFAULT@SECLEVEL=1"
+```
+
+
+
+```yaml
+litellm_settings:
+ ssl_security_level: "DEFAULT@SECLEVEL=1"
+```
+
+
+
+```bash
+export SSL_SECURITY_LEVEL="DEFAULT@SECLEVEL=1"
+```
+
+
+
+## 4. Certificate authentication
+
+The `SSL_CERTIFICATE` environmental variable or `ssl_certificate` attribute allows setting a client side certificate to authenticate the client to the server.
```python
import litellm
-litellm.ssl_security_level = 1
litellm.ssl_certificate = "/path/to/certificate.pem"
```
@@ -50,20 +109,18 @@ litellm.ssl_certificate = "/path/to/certificate.pem"
```yaml
litellm_settings:
- ssl_security_level: 1
ssl_certificate: "/path/to/certificate.pem"
```
```bash
-export SSL_SECURITY_LEVEL="1"
export SSL_CERTIFICATE="/path/to/certificate.pem"
```
-## 3. Use HTTP_PROXY environment variable
+## 5. Use HTTP_PROXY environment variable
Both httpx and aiohttp libraries use `urllib.request.getproxies` from environment variables. Before client initialization, you may set proxy (and optional SSL_CERT_FILE) by setting the environment variables:
diff --git a/docs/my-website/docs/image_generation.md b/docs/my-website/docs/image_generation.md
index 5af3e10e0ca..46cf288f5a4 100644
--- a/docs/my-website/docs/image_generation.md
+++ b/docs/my-website/docs/image_generation.md
@@ -52,7 +52,7 @@ litellm --config /path/to/config.yaml
curl -X POST 'http://0.0.0.0:4000/v1/images/generations' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer sk-1234' \
--D '{
+-d '{
"model": "gpt-image-1",
"prompt": "A cute baby sea otter",
"n": 1,
diff --git a/docs/my-website/docs/mcp.md b/docs/my-website/docs/mcp.md
index 3a4de87becc..66017667966 100644
--- a/docs/my-website/docs/mcp.md
+++ b/docs/my-website/docs/mcp.md
@@ -83,7 +83,6 @@ mcp_servers:
-
## Using your MCP
@@ -159,7 +158,7 @@ Use tools directly from Cursor IDE with LiteLLM MCP:
2. **Navigate to MCP Tools**: Go to the "MCP Tools" tab and click "New MCP Server"
3. **Add Configuration**: Copy and paste the JSON configuration below, then save with `Cmd+S` or `Ctrl+S`
-```json title="Cursor MCP Configuration" showLineNumbers
+```json title="Basic Cursor MCP Configuration" showLineNumbers
{
"mcpServers": {
"LiteLLM": {
@@ -173,98 +172,100 @@ Use tools directly from Cursor IDE with LiteLLM MCP:
```
+
-
+## Segregating MCP Server Access
-#### Connect via Streamable HTTP Transport
+You can choose to access specific MCP servers and only list their tools using the `x-mcp-servers` header. This header allows you to:
+- Limit tool access to one or more specific MCP servers
+- Control which tools are available in different environments or use cases
-Connect to LiteLLM MCP using HTTP transport. Compatible with any MCP client that supports HTTP streaming:
+The header accepts a comma-separated list of server names: `"Zapier_Gmail,Server2,Server3"`
-**Server URL:**
-```text showLineNumbers
-/mcp
+Notes:
+- Server names with spaces should be replaced with underscores
+- If the header is not provided, tools from all available MCP servers will be accessible
+
+
+
+
+```bash title="cURL Example with Server Segregation" showLineNumbers
+curl --location 'https://api.openai.com/v1/responses' \
+--header 'Content-Type: application/json' \
+--header "Authorization: Bearer $OPENAI_API_KEY" \
+--data '{
+ "model": "gpt-4o",
+ "tools": [
+ {
+ "type": "mcp",
+ "server_label": "litellm",
+ "server_url": "/mcp",
+ "require_approval": "never",
+ "headers": {
+ "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail"
+ }
+ }
+ ],
+ "input": "Run available tools",
+ "tool_choice": "required"
+}'
```
-**Headers:**
-```text showLineNumbers
-x-litellm-api-key: Bearer YOUR_LITELLM_API_KEY
-```
-
-This URL can be used with any MCP client that supports HTTP transport. Refer to your client documentation to determine the appropriate transport method.
+In this example, the request will only have access to tools from the "Zapier_Gmail" MCP server.
-
+
-#### Connect via Python FastMCP Client
-
-Use the Python FastMCP client to connect to your LiteLLM MCP server:
-
-**Installation:**
-
-```bash title="Install FastMCP" showLineNumbers
-pip install fastmcp
+```bash title="cURL Example with Server Segregation" showLineNumbers
+curl --location '/v1/responses' \
+--header 'Content-Type: application/json' \
+--header "Authorization: Bearer $LITELLM_API_KEY" \
+--data '{
+ "model": "gpt-4o",
+ "tools": [
+ {
+ "type": "mcp",
+ "server_label": "litellm",
+ "server_url": "/mcp",
+ "require_approval": "never",
+ "headers": {
+ "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail,Server2"
+ }
+ }
+ ],
+ "input": "Run available tools",
+ "tool_choice": "required"
+}'
```
-or with uv:
+This configuration restricts the request to only use tools from the specified MCP servers.
-```bash title="Install with uv" showLineNumbers
-uv pip install fastmcp
-```
+
-**Usage:**
+
-```python title="Python FastMCP Example" showLineNumbers
-import asyncio
-import json
-
-from fastmcp import Client
-from fastmcp.client.transports import StreamableHttpTransport
-
-# Create the transport with your LiteLLM MCP server URL
-server_url = "/mcp"
-transport = StreamableHttpTransport(
- server_url,
- headers={
- "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY"
+```json title="Cursor MCP Configuration with Server Segregation" showLineNumbers
+{
+ "mcpServers": {
+ "LiteLLM": {
+ "url": "/mcp",
+ "headers": {
+ "x-litellm-api-key": "Bearer $LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail,Server2"
+ }
}
-)
-
-# Initialize the client with the transport
-client = Client(transport=transport)
-
-
-async def main():
- # Connection is established here
- print("Connecting to LiteLLM MCP server...")
- async with client:
- print(f"Client connected: {client.is_connected()}")
-
- # Make MCP calls within the context
- print("Fetching available tools...")
- tools = await client.list_tools()
-
- print(f"Available tools: {json.dumps([t.name for t in tools], indent=2)}")
-
- # Example: Call a tool (replace 'tool_name' with an actual tool name)
- if tools:
- tool_name = tools[0].name
- print(f"Calling tool: {tool_name}")
-
- # Call the tool with appropriate arguments
- result = await client.call_tool(tool_name, arguments={})
- print(f"Tool result: {result}")
-
-
-# Run the example
-if __name__ == "__main__":
- asyncio.run(main())
+ }
+}
```
+This configuration in Cursor IDE settings will limit tool access to only the specified MCP server.
+
-
## Using your MCP with client side credentials
Use this if you want to pass a client side authentication token to LiteLLM to then pass to your MCP to auth to your MCP.
@@ -715,4 +716,4 @@ async with stdio_client(server_params) as (read, write):
```
-
+
\ No newline at end of file
diff --git a/docs/my-website/docs/observability/langfuse_integration.md b/docs/my-website/docs/observability/langfuse_integration.md
index 34b213f0e21..b6d8c836520 100644
--- a/docs/my-website/docs/observability/langfuse_integration.md
+++ b/docs/my-website/docs/observability/langfuse_integration.md
@@ -11,6 +11,13 @@ Example trace in Langfuse using multiple models via LiteLLM:
+:::info
+
+For Langfuse v3, we recommend using the [Langfuse OTEL](./langfuse_otel_integration) integration.
+
+:::
+
+
## Usage with LiteLLM Proxy (LLM Gateway)
👉 [**Follow this link to start sending logs to langfuse with LiteLLM Proxy server**](../proxy/logging)
diff --git a/docs/my-website/docs/observability/langfuse_otel_integration.md b/docs/my-website/docs/observability/langfuse_otel_integration.md
index 267738c3003..c45c33f0f25 100644
--- a/docs/my-website/docs/observability/langfuse_otel_integration.md
+++ b/docs/my-website/docs/observability/langfuse_otel_integration.md
@@ -1,7 +1,14 @@
-# Langfuse OpenTelemetry Integration
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+import Image from '@theme/IdealImage';
+
+# 🪢 Langfuse OpenTelemetry Integration
The Langfuse OpenTelemetry integration allows you to send LiteLLM traces and observability data to Langfuse using the OpenTelemetry protocol. This provides a standardized way to collect and analyze your LLM usage data.
+
+
## Features
- Automatic trace collection for all LiteLLM requests
@@ -108,15 +115,26 @@ litellm.callbacks = ["langfuse_otel"]
Add the integration to your proxy configuration:
+1. Add the credentials to your environment variables
+
+```bash
+export LANGFUSE_PUBLIC_KEY="pk-lf-..."
+export LANGFUSE_SECRET_KEY="sk-lf-..."
+export LANGFUSE_HOST="https://us.cloud.langfuse.com" # Default US region
+```
+
+2. Setup config.yaml
+
```yaml
# config.yaml
litellm_settings:
callbacks: ["langfuse_otel"]
+```
-environment_variables:
- LANGFUSE_PUBLIC_KEY: "pk-lf-..."
- LANGFUSE_SECRET_KEY: "sk-lf-..."
- LANGFUSE_HOST: "https://us.cloud.langfuse.com" # Default US region
+3. Run the proxy
+
+```bash
+litellm --config /path/to/config.yaml
```
## Data Collected
@@ -163,11 +181,24 @@ This is automatically handled by the integration - you just need to provide the
Enable verbose logging to see detailed information:
+
+
+
```python
import litellm
-litellm.set_verbose = True
+litellm._turn_on_debug()
```
+
+
+
+```bash
+export LITELLM_LOG="DEBUG"
+```
+
+
+
+
This will show:
- Endpoint resolution logic
- Authentication header creation
diff --git a/docs/my-website/docs/observability/opentelemetry_integration.md b/docs/my-website/docs/observability/opentelemetry_integration.md
index 958c33f18e6..23532ab6e80 100644
--- a/docs/my-website/docs/observability/opentelemetry_integration.md
+++ b/docs/my-website/docs/observability/opentelemetry_integration.md
@@ -104,4 +104,14 @@ for successful + failed requests
click under `litellm_request` in the trace
-
\ No newline at end of file
+
+
+### Not seeing traces land on Integration
+
+If you don't see traces landing on your integration, set `OTEL_DEBUG="True"` in your LiteLLM environment and try again.
+
+```shell
+export OTEL_DEBUG="True"
+```
+
+This will emit any logging issues to the console.
\ No newline at end of file
diff --git a/docs/my-website/docs/old_guardrails.md b/docs/my-website/docs/old_guardrails.md
index 451ca8ab508..73448666c43 100644
--- a/docs/my-website/docs/old_guardrails.md
+++ b/docs/my-website/docs/old_guardrails.md
@@ -212,7 +212,7 @@ If you need to switch `pii_masking` off for an API Key set `"permissions": {"pii
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"permissions": {"pii_masking": true}
}'
```
diff --git a/docs/my-website/docs/providers/azure_ai.md b/docs/my-website/docs/providers/azure_ai.md
index 60f7ecb2a5c..b1b5de5bb34 100644
--- a/docs/my-website/docs/providers/azure_ai.md
+++ b/docs/my-website/docs/providers/azure_ai.md
@@ -339,7 +339,7 @@ documents = [
]
response = rerank(
- model="azure_ai/rerank-english-v3.0",
+ model="azure_ai/cohere-rerank-v3.5",
query=query,
documents=documents,
top_n=3,
@@ -362,9 +362,9 @@ model_list:
litellm_params:
model: together_ai/Salesforce/Llama-Rank-V1
api_key: os.environ/TOGETHERAI_API_KEY
- - model_name: rerank-english-v3.0
+ - model_name: cohere-rerank-v3.5
litellm_params:
- model: azure_ai/rerank-english-v3.0
+ model: azure_ai/cohere-rerank-v3.5
api_key: os.environ/AZURE_AI_API_KEY
api_base: os.environ/AZURE_AI_API_BASE
```
@@ -384,7 +384,7 @@ curl http://0.0.0.0:4000/rerank \
-H "Authorization: Bearer sk-1234" \
-H "Content-Type: application/json" \
-d '{
- "model": "rerank-english-v3.0",
+ "model": "cohere-rerank-v3.5",
"query": "What is the capital of the United States?",
"documents": [
"Carson City is the capital city of the American state of Nevada.",
diff --git a/docs/my-website/docs/providers/bedrock_agents.md b/docs/my-website/docs/providers/bedrock_agents.md
index e6368705feb..4d027cbb3d8 100644
--- a/docs/my-website/docs/providers/bedrock_agents.md
+++ b/docs/my-website/docs/providers/bedrock_agents.md
@@ -196,7 +196,51 @@ for chunk in stream:
+## Provider-specific Parameters
+
+Any non-openai parameters will be passed to the agent as custom parameters.
+
+
+
+
+```python showLineNumbers title="Using custom parameters"
+from litellm import completion
+
+response = litellm.completion(
+ model="bedrock/agent/L1RT58GYRW/MFPSBCXYTW",
+ messages=[
+ {
+ "role": "user",
+ "content": "Hi who is ishaan cto of litellm, tell me 10 things about him",
+ }
+ ],
+ invocationId="my-test-invocation-id", # PROVIDER-SPECIFIC VALUE
+)
+```
+
+
+
+
+```yaml showLineNumbers title="LiteLLM Proxy Configuration"
+model_list:
+ - model_name: bedrock-agent-1
+ litellm_params:
+ model: bedrock/agent/L1RT58GYRW/MFPSBCXYTW
+ aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID
+ aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY
+ aws_region_name: us-west-2
+ invocationId: my-test-invocation-id
+```
+
+
+
+
+
+
+
+
## Further Reading
- [AWS Bedrock Agents Documentation](https://aws.amazon.com/bedrock/agents/)
- [LiteLLM Authentication to Bedrock](https://docs.litellm.ai/docs/providers/bedrock#boto3---authentication)
+
diff --git a/docs/my-website/docs/providers/github_copilot.md b/docs/my-website/docs/providers/github_copilot.md
new file mode 100644
index 00000000000..2ebe6eacb1c
--- /dev/null
+++ b/docs/my-website/docs/providers/github_copilot.md
@@ -0,0 +1,186 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# GitHub Copilot
+
+https://docs.github.com/en/copilot
+
+:::tip
+
+**We support GitHub Copilot Chat API with automatic authentication handling**
+
+:::
+
+| Property | Details |
+|-------|-------|
+| Description | GitHub Copilot Chat API provides access to GitHub's AI-powered coding assistant. |
+| Provider Route on LiteLLM | `github_copilot/` |
+| Supported Endpoints | `/chat/completions` |
+| API Reference | [GitHub Copilot docs](https://docs.github.com/en/copilot) |
+
+## Authentication
+
+GitHub Copilot uses OAuth device flow for authentication. On first use, you'll be prompted to authenticate via GitHub:
+
+1. LiteLLM will display a device code and verification URL
+2. Visit the URL and enter the code to authenticate
+3. Your credentials will be stored locally for future use
+
+## Usage - LiteLLM Python SDK
+
+### Chat Completion
+
+```python showLineNumbers title="GitHub Copilot Chat Completion"
+from litellm import completion
+
+response = completion(
+ model="github_copilot/gpt-4",
+ messages=[{"role": "user", "content": "Write a Python function to calculate fibonacci numbers"}],
+ extra_headers={
+ "editor-version": "vscode/1.85.1",
+ "Copilot-Integration-Id": "vscode-chat"
+ }
+)
+print(response)
+```
+
+```python showLineNumbers title="GitHub Copilot Chat Completion - Streaming"
+from litellm import completion
+
+stream = completion(
+ model="github_copilot/gpt-4",
+ messages=[{"role": "user", "content": "Explain async/await in Python"}],
+ stream=True,
+ extra_headers={
+ "editor-version": "vscode/1.85.1",
+ "Copilot-Integration-Id": "vscode-chat"
+ }
+)
+
+for chunk in stream:
+ if chunk.choices[0].delta.content is not None:
+ print(chunk.choices[0].delta.content, end="")
+```
+
+## Usage - LiteLLM Proxy
+
+Add the following to your LiteLLM Proxy configuration file:
+
+```yaml showLineNumbers title="config.yaml"
+model_list:
+ - model_name: github_copilot/gpt-4
+ litellm_params:
+ model: github_copilot/gpt-4
+```
+
+Start your LiteLLM Proxy server:
+
+```bash showLineNumbers title="Start LiteLLM Proxy"
+litellm --config config.yaml
+
+# RUNNING on http://0.0.0.0:4000
+```
+
+
+
+
+```python showLineNumbers title="GitHub Copilot via Proxy - Non-streaming"
+from openai import OpenAI
+
+# Initialize client with your proxy URL
+client = OpenAI(
+ base_url="http://localhost:4000", # Your proxy URL
+ api_key="your-proxy-api-key" # Your proxy API key
+)
+
+# Non-streaming response
+response = client.chat.completions.create(
+ model="github_copilot/gpt-4",
+ messages=[{"role": "user", "content": "How do I optimize this SQL query?"}],
+ extra_headers={
+ "editor-version": "vscode/1.85.1",
+ "Copilot-Integration-Id": "vscode-chat"
+ }
+)
+
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+```python showLineNumbers title="GitHub Copilot via Proxy - LiteLLM SDK"
+import litellm
+
+# Configure LiteLLM to use your proxy
+response = litellm.completion(
+ model="litellm_proxy/github_copilot/gpt-4",
+ messages=[{"role": "user", "content": "Review this code for bugs"}],
+ api_base="http://localhost:4000",
+ api_key="your-proxy-api-key",
+ extra_headers={
+ "editor-version": "vscode/1.85.1",
+ "Copilot-Integration-Id": "vscode-chat"
+ }
+)
+
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+```bash showLineNumbers title="GitHub Copilot via Proxy - cURL"
+curl http://localhost:4000/v1/chat/completions \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer your-proxy-api-key" \
+ -H "editor-version: vscode/1.85.1" \
+ -H "Copilot-Integration-Id: vscode-chat" \
+ -d '{
+ "model": "github_copilot/gpt-4",
+ "messages": [{"role": "user", "content": "Explain this error message"}]
+ }'
+```
+
+
+
+
+## Getting Started
+
+1. Ensure you have GitHub Copilot access (paid GitHub subscription required)
+2. Run your first LiteLLM request - you'll be prompted to authenticate
+3. Follow the device flow authentication process
+4. Start making requests to GitHub Copilot through LiteLLM
+
+## Configuration
+
+### Environment Variables
+
+You can customize token storage locations:
+
+```bash showLineNumbers title="Environment Variables"
+# Optional: Custom token directory
+export GITHUB_COPILOT_TOKEN_DIR="~/.config/litellm/github_copilot"
+
+# Optional: Custom access token file name
+export GITHUB_COPILOT_ACCESS_TOKEN_FILE="access-token"
+
+# Optional: Custom API key file name
+export GITHUB_COPILOT_API_KEY_FILE="api-key.json"
+```
+
+### Headers
+
+GitHub Copilot supports various editor-specific headers:
+
+```python showLineNumbers title="Common Headers"
+extra_headers = {
+ "editor-version": "vscode/1.85.1", # Editor version
+ "editor-plugin-version": "copilot/1.155.0", # Plugin version
+ "Copilot-Integration-Id": "vscode-chat", # Integration ID
+ "user-agent": "GithubCopilot/1.155.0" # User agent
+}
+```
+
diff --git a/docs/my-website/docs/providers/vertex.md b/docs/my-website/docs/providers/vertex.md
index 21c17933b1b..fda0cee8626 100644
--- a/docs/my-website/docs/providers/vertex.md
+++ b/docs/my-website/docs/providers/vertex.md
@@ -2,7 +2,7 @@ import Image from '@theme/IdealImage';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# VertexAI [Anthropic, Gemini, Model Garden]
+# VertexAI [Gemini]
## Overview
@@ -1208,534 +1208,6 @@ os.environ["VERTEXAI_LOCATION"] = "us-central1 # Your Location
# set directly on module
litellm.vertex_location = "us-central1 # Your Location
```
-## Anthropic
-| Model Name | Function Call |
-|------------------|--------------------------------------|
-| claude-3-opus@20240229 | `completion('vertex_ai/claude-3-opus@20240229', messages)` |
-| claude-3-5-sonnet@20240620 | `completion('vertex_ai/claude-3-5-sonnet@20240620', messages)` |
-| claude-3-sonnet@20240229 | `completion('vertex_ai/claude-3-sonnet@20240229', messages)` |
-| claude-3-haiku@20240307 | `completion('vertex_ai/claude-3-haiku@20240307', messages)` |
-| claude-3-7-sonnet@20250219 | `completion('vertex_ai/claude-3-7-sonnet@20250219', messages)` |
-
-### Usage
-
-
-
-
-```python
-from litellm import completion
-import os
-
-os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
-
-model = "claude-3-sonnet@20240229"
-
-vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
-vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
-
-response = completion(
- model="vertex_ai/" + model,
- messages=[{"role": "user", "content": "hi"}],
- temperature=0.7,
- vertex_ai_project=vertex_ai_project,
- vertex_ai_location=vertex_ai_location,
-)
-print("\nModel Response", response)
-```
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: anthropic-vertex
- litellm_params:
- model: vertex_ai/claude-3-sonnet@20240229
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
- - model_name: anthropic-vertex
- litellm_params:
- model: vertex_ai/claude-3-sonnet@20240229
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "anthropic-vertex", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-
-
-### Usage - `thinking` / `reasoning_content`
-
-
-
-
-
-```python
-from litellm import completion
-
-resp = completion(
- model="vertex_ai/claude-3-7-sonnet-20250219",
- messages=[{"role": "user", "content": "What is the capital of France?"}],
- thinking={"type": "enabled", "budget_tokens": 1024},
-)
-
-```
-
-
-
-
-
-1. Setup config.yaml
-
-```yaml
-- model_name: claude-3-7-sonnet-20250219
- litellm_params:
- model: vertex_ai/claude-3-7-sonnet-20250219
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-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 " \
- -d '{
- "model": "claude-3-7-sonnet-20250219",
- "messages": [{"role": "user", "content": "What is the capital of France?"}],
- "thinking": {"type": "enabled", "budget_tokens": 1024}
- }'
-```
-
-
-
-
-
-**Expected Response**
-
-```python
-ModelResponse(
- id='chatcmpl-c542d76d-f675-4e87-8e5f-05855f5d0f5e',
- created=1740470510,
- model='claude-3-7-sonnet-20250219',
- object='chat.completion',
- system_fingerprint=None,
- choices=[
- Choices(
- finish_reason='stop',
- index=0,
- message=Message(
- content="The capital of France is Paris.",
- role='assistant',
- tool_calls=None,
- function_call=None,
- provider_specific_fields={
- 'citations': None,
- 'thinking_blocks': [
- {
- 'type': 'thinking',
- 'thinking': 'The capital of France is Paris. This is a very straightforward factual question.',
- 'signature': 'EuYBCkQYAiJAy6...'
- }
- ]
- }
- ),
- thinking_blocks=[
- {
- 'type': 'thinking',
- 'thinking': 'The capital of France is Paris. This is a very straightforward factual question.',
- 'signature': 'EuYBCkQYAiJAy6AGB...'
- }
- ],
- reasoning_content='The capital of France is Paris. This is a very straightforward factual question.'
- )
- ],
- usage=Usage(
- completion_tokens=68,
- prompt_tokens=42,
- total_tokens=110,
- completion_tokens_details=None,
- prompt_tokens_details=PromptTokensDetailsWrapper(
- audio_tokens=None,
- cached_tokens=0,
- text_tokens=None,
- image_tokens=None
- ),
- cache_creation_input_tokens=0,
- cache_read_input_tokens=0
- )
-)
-```
-
-
-
-## Meta/Llama API
-
-| Model Name | Function Call |
-|------------------|--------------------------------------|
-| meta/llama-3.2-90b-vision-instruct-maas | `completion('vertex_ai/meta/llama-3.2-90b-vision-instruct-maas', messages)` |
-| meta/llama3-8b-instruct-maas | `completion('vertex_ai/meta/llama3-8b-instruct-maas', messages)` |
-| meta/llama3-70b-instruct-maas | `completion('vertex_ai/meta/llama3-70b-instruct-maas', messages)` |
-| meta/llama3-405b-instruct-maas | `completion('vertex_ai/meta/llama3-405b-instruct-maas', messages)` |
-| meta/llama-4-scout-17b-16e-instruct-maas | `completion('vertex_ai/meta/llama-4-scout-17b-16e-instruct-maas', messages)` |
-| meta/llama-4-scout-17-128e-instruct-maas | `completion('vertex_ai/meta/llama-4-scout-128b-16e-instruct-maas', messages)` |
-| meta/llama-4-maverick-17b-128e-instruct-maas | `completion('vertex_ai/meta/llama-4-maverick-17b-128e-instruct-maas',messages)` |
-| meta/llama-4-maverick-17b-16e-instruct-maas | `completion('vertex_ai/meta/llama-4-maverick-17b-16e-instruct-maas',messages)` |
-
-### Usage
-
-
-
-
-```python
-from litellm import completion
-import os
-
-os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
-
-model = "meta/llama3-405b-instruct-maas"
-
-vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
-vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
-
-response = completion(
- model="vertex_ai/" + model,
- messages=[{"role": "user", "content": "hi"}],
- vertex_ai_project=vertex_ai_project,
- vertex_ai_location=vertex_ai_location,
-)
-print("\nModel Response", response)
-```
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: anthropic-llama
- litellm_params:
- model: vertex_ai/meta/llama3-405b-instruct-maas
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
- - model_name: anthropic-llama
- litellm_params:
- model: vertex_ai/meta/llama3-405b-instruct-maas
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "anthropic-llama", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-## Mistral API
-
-[**Supported OpenAI Params**](https://github.com/BerriAI/litellm/blob/e0f3cd580cb85066f7d36241a03c30aa50a8a31d/litellm/llms/openai.py#L137)
-
-| Model Name | Function Call |
-|------------------|--------------------------------------|
-| mistral-large@latest | `completion('vertex_ai/mistral-large@latest', messages)` |
-| mistral-large@2407 | `completion('vertex_ai/mistral-large@2407', messages)` |
-| mistral-nemo@latest | `completion('vertex_ai/mistral-nemo@latest', messages)` |
-| codestral@latest | `completion('vertex_ai/codestral@latest', messages)` |
-| codestral@@2405 | `completion('vertex_ai/codestral@2405', messages)` |
-
-### Usage
-
-
-
-
-```python
-from litellm import completion
-import os
-
-os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
-
-model = "mistral-large@2407"
-
-vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
-vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
-
-response = completion(
- model="vertex_ai/" + model,
- messages=[{"role": "user", "content": "hi"}],
- vertex_ai_project=vertex_ai_project,
- vertex_ai_location=vertex_ai_location,
-)
-print("\nModel Response", response)
-```
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: vertex-mistral
- litellm_params:
- model: vertex_ai/mistral-large@2407
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
- - model_name: vertex-mistral
- litellm_params:
- model: vertex_ai/mistral-large@2407
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "vertex-mistral", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-
-### Usage - Codestral FIM
-
-Call Codestral on VertexAI via the OpenAI [`/v1/completion`](https://platform.openai.com/docs/api-reference/completions/create) endpoint for FIM tasks.
-
-Note: You can also call Codestral via `/chat/completion`.
-
-
-
-
-```python
-from litellm import completion
-import os
-
-# os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
-# OR run `!gcloud auth print-access-token` in your terminal
-
-model = "codestral@2405"
-
-vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
-vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
-
-response = text_completion(
- model="vertex_ai/" + model,
- vertex_ai_project=vertex_ai_project,
- vertex_ai_location=vertex_ai_location,
- prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():",
- suffix="return True", # optional
- temperature=0, # optional
- top_p=1, # optional
- max_tokens=10, # optional
- min_tokens=10, # optional
- seed=10, # optional
- stop=["return"], # optional
-)
-
-print("\nModel Response", response)
-```
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: vertex-codestral
- litellm_params:
- model: vertex_ai/codestral@2405
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
- - model_name: vertex-codestral
- litellm_params:
- model: vertex_ai/codestral@2405
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl -X POST 'http://0.0.0.0:4000/completions' \
- -H 'Authorization: Bearer sk-1234' \
- -H 'Content-Type: application/json' \
- -d '{
- "model": "vertex-codestral", # 👈 the 'model_name' in config
- "prompt": "def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():",
- "suffix":"return True", # optional
- "temperature":0, # optional
- "top_p":1, # optional
- "max_tokens":10, # optional
- "min_tokens":10, # optional
- "seed":10, # optional
- "stop":["return"], # optional
- }'
-```
-
-
-
-
-
-## AI21 Models
-
-| Model Name | Function Call |
-|------------------|--------------------------------------|
-| jamba-1.5-mini@001 | `completion(model='vertex_ai/jamba-1.5-mini@001', messages)` |
-| jamba-1.5-large@001 | `completion(model='vertex_ai/jamba-1.5-large@001', messages)` |
-
-### Usage
-
-
-
-
-```python
-from litellm import completion
-import os
-
-os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
-
-model = "meta/jamba-1.5-mini@001"
-
-vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
-vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
-
-response = completion(
- model="vertex_ai/" + model,
- messages=[{"role": "user", "content": "hi"}],
- vertex_ai_project=vertex_ai_project,
- vertex_ai_location=vertex_ai_location,
-)
-print("\nModel Response", response)
-```
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: jamba-1.5-mini
- litellm_params:
- model: vertex_ai/jamba-1.5-mini@001
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
- - model_name: jamba-1.5-large
- litellm_params:
- model: vertex_ai/jamba-1.5-large@001
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-west-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "jamba-1.5-large",
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
## Gemini Pro
| Model Name | Function Call |
@@ -1832,119 +1304,6 @@ curl --location 'https://0.0.0.0:4000/v1/chat/completions' \
-
-
-## Model Garden
-
-:::tip
-
-All OpenAI compatible models from Vertex Model Garden are supported.
-
-:::
-
-#### Using Model Garden
-
-**Almost all Vertex Model Garden models are OpenAI compatible.**
-
-
-
-
-
-| Property | Details |
-|----------|---------|
-| Provider Route | `vertex_ai/openai/{MODEL_ID}` |
-| Vertex Documentation | [Vertex Model Garden - OpenAI Chat Completions](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_gradio_streaming_chat_completions.ipynb), [Vertex Model Garden](https://cloud.google.com/model-garden?hl=en) |
-| Supported Operations | `/chat/completions`, `/embeddings` |
-
-
-
-
-```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/openai/",
- messages=[{ "content": "Hello, how are you?","role": "user"}]
-)
-```
-
-
-
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: llama3-1-8b-instruct
- litellm_params:
- model: vertex_ai/openai/5464397967697903616
- vertex_ai_project: "my-test-project"
- vertex_ai_location: "us-east-1"
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "llama3-1-8b-instruct", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-
-
-
-
-
-
-
-
-```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/",
- messages=[{ "content": "Hello, how are you?","role": "user"}]
-)
-```
-
-
-
-
-
-
-
## Gemini Pro Vision
| Model Name | Function Call |
|------------------|--------------------------------------|
diff --git a/docs/my-website/docs/providers/vertex_partner.md b/docs/my-website/docs/providers/vertex_partner.md
new file mode 100644
index 00000000000..09e08942f9b
--- /dev/null
+++ b/docs/my-website/docs/providers/vertex_partner.md
@@ -0,0 +1,670 @@
+import Image from '@theme/IdealImage';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+
+# Vertex AI - Anthropic, DeepSeek, Model Garden
+
+## Supported Partner Providers
+
+| Provider | LiteLLM Route | Vertex Documentation |
+|----------|---------------|---------------|
+| Anthropic (Claude) | `vertex_ai/claude-*` | [Vertex AI - Anthropic Models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude) |
+| DeepSeek | `vertex_ai/deepseek-ai/{MODEL}` | [Vertex AI - DeepSeek Models](https://cloud.google.com/vertex-ai/generative-ai/docs/maas/deepseek) |
+| Meta/Llama | `vertex_ai/meta/{MODEL}` | [Vertex AI - Meta Models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/llama) |
+| Mistral | `vertex_ai/mistral-*` | [Vertex AI - Mistral Models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/mistral) |
+| AI21 (Jamba) | `vertex_ai/jamba-*` | [Vertex AI - AI21 Models](https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/ai21) |
+| Model Garden | `vertex_ai/openai/{MODEL_ID}` or `vertex_ai/{MODEL_ID}` | [Vertex Model Garden](https://cloud.google.com/model-garden?hl=en) |
+
+## Vertex AI - Anthropic (Claude)
+
+| Model Name | Function Call |
+|------------------|--------------------------------------|
+| claude-3-opus@20240229 | `completion('vertex_ai/claude-3-opus@20240229', messages)` |
+| claude-3-5-sonnet@20240620 | `completion('vertex_ai/claude-3-5-sonnet@20240620', messages)` |
+| claude-3-sonnet@20240229 | `completion('vertex_ai/claude-3-sonnet@20240229', messages)` |
+| claude-3-haiku@20240307 | `completion('vertex_ai/claude-3-haiku@20240307', messages)` |
+| claude-3-7-sonnet@20250219 | `completion('vertex_ai/claude-3-7-sonnet@20250219', messages)` |
+
+#### Usage
+
+
+
+
+```python
+from litellm import completion
+import os
+
+os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
+
+model = "claude-3-sonnet@20240229"
+
+vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
+vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
+
+response = completion(
+ model="vertex_ai/" + model,
+ messages=[{"role": "user", "content": "hi"}],
+ temperature=0.7,
+ vertex_ai_project=vertex_ai_project,
+ vertex_ai_location=vertex_ai_location,
+)
+print("\nModel Response", response)
+```
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: anthropic-vertex
+ litellm_params:
+ model: vertex_ai/claude-3-sonnet@20240229
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+ - model_name: anthropic-vertex
+ litellm_params:
+ model: vertex_ai/claude-3-sonnet@20240229
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "anthropic-vertex", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+
+
+#### Usage - `thinking` / `reasoning_content`
+
+
+
+
+
+```python
+from litellm import completion
+
+resp = completion(
+ model="vertex_ai/claude-3-7-sonnet-20250219",
+ messages=[{"role": "user", "content": "What is the capital of France?"}],
+ thinking={"type": "enabled", "budget_tokens": 1024},
+)
+
+```
+
+
+
+
+
+1. Setup config.yaml
+
+```yaml
+- model_name: claude-3-7-sonnet-20250219
+ litellm_params:
+ model: vertex_ai/claude-3-7-sonnet-20250219
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+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 " \
+ -d '{
+ "model": "claude-3-7-sonnet-20250219",
+ "messages": [{"role": "user", "content": "What is the capital of France?"}],
+ "thinking": {"type": "enabled", "budget_tokens": 1024}
+ }'
+```
+
+
+
+
+
+**Expected Response**
+
+```python
+ModelResponse(
+ id='chatcmpl-c542d76d-f675-4e87-8e5f-05855f5d0f5e',
+ created=1740470510,
+ model='claude-3-7-sonnet-20250219',
+ object='chat.completion',
+ system_fingerprint=None,
+ choices=[
+ Choices(
+ finish_reason='stop',
+ index=0,
+ message=Message(
+ content="The capital of France is Paris.",
+ role='assistant',
+ tool_calls=None,
+ function_call=None,
+ provider_specific_fields={
+ 'citations': None,
+ 'thinking_blocks': [
+ {
+ 'type': 'thinking',
+ 'thinking': 'The capital of France is Paris. This is a very straightforward factual question.',
+ 'signature': 'EuYBCkQYAiJAy6...'
+ }
+ ]
+ }
+ ),
+ thinking_blocks=[
+ {
+ 'type': 'thinking',
+ 'thinking': 'The capital of France is Paris. This is a very straightforward factual question.',
+ 'signature': 'EuYBCkQYAiJAy6AGB...'
+ }
+ ],
+ reasoning_content='The capital of France is Paris. This is a very straightforward factual question.'
+ )
+ ],
+ usage=Usage(
+ completion_tokens=68,
+ prompt_tokens=42,
+ total_tokens=110,
+ completion_tokens_details=None,
+ prompt_tokens_details=PromptTokensDetailsWrapper(
+ audio_tokens=None,
+ cached_tokens=0,
+ text_tokens=None,
+ image_tokens=None
+ ),
+ cache_creation_input_tokens=0,
+ cache_read_input_tokens=0
+ )
+)
+```
+
+## VertexAI DeepSeek
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `vertex_ai/deepseek-ai/{MODEL}` |
+| Vertex Documentation | [Vertex AI - DeepSeek Models](https://cloud.google.com/vertex-ai/generative-ai/docs/maas/deepseek) |
+
+#### Usage
+
+**LiteLLM Supports all Vertex AI DeepSeek Models.** Ensure you use the `vertex_ai/deepseek-ai/` prefix for all Vertex AI DeepSeek models.
+
+| Model Name | Usage |
+|------------------|------------------------------|
+| vertex_ai/deepseek-ai/deepseek-r1-0528-maas | `completion('vertex_ai/deepseek-ai/deepseek-r1-0528-maas', messages)` |
+
+
+## VertexAI Meta/Llama API
+
+| Model Name | Function Call |
+|------------------|--------------------------------------|
+| meta/llama-3.2-90b-vision-instruct-maas | `completion('vertex_ai/meta/llama-3.2-90b-vision-instruct-maas', messages)` |
+| meta/llama3-8b-instruct-maas | `completion('vertex_ai/meta/llama3-8b-instruct-maas', messages)` |
+| meta/llama3-70b-instruct-maas | `completion('vertex_ai/meta/llama3-70b-instruct-maas', messages)` |
+| meta/llama3-405b-instruct-maas | `completion('vertex_ai/meta/llama3-405b-instruct-maas', messages)` |
+| meta/llama-4-scout-17b-16e-instruct-maas | `completion('vertex_ai/meta/llama-4-scout-17b-16e-instruct-maas', messages)` |
+| meta/llama-4-scout-17-128e-instruct-maas | `completion('vertex_ai/meta/llama-4-scout-128b-16e-instruct-maas', messages)` |
+| meta/llama-4-maverick-17b-128e-instruct-maas | `completion('vertex_ai/meta/llama-4-maverick-17b-128e-instruct-maas',messages)` |
+| meta/llama-4-maverick-17b-16e-instruct-maas | `completion('vertex_ai/meta/llama-4-maverick-17b-16e-instruct-maas',messages)` |
+
+#### Usage
+
+
+
+
+```python
+from litellm import completion
+import os
+
+os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
+
+model = "meta/llama3-405b-instruct-maas"
+
+vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
+vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
+
+response = completion(
+ model="vertex_ai/" + model,
+ messages=[{"role": "user", "content": "hi"}],
+ vertex_ai_project=vertex_ai_project,
+ vertex_ai_location=vertex_ai_location,
+)
+print("\nModel Response", response)
+```
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: anthropic-llama
+ litellm_params:
+ model: vertex_ai/meta/llama3-405b-instruct-maas
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+ - model_name: anthropic-llama
+ litellm_params:
+ model: vertex_ai/meta/llama3-405b-instruct-maas
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "anthropic-llama", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+## VertexAI Mistral API
+
+[**Supported OpenAI Params**](https://github.com/BerriAI/litellm/blob/e0f3cd580cb85066f7d36241a03c30aa50a8a31d/litellm/llms/openai.py#L137)
+
+| Model Name | Function Call |
+|------------------|--------------------------------------|
+| mistral-large@latest | `completion('vertex_ai/mistral-large@latest', messages)` |
+| mistral-large@2407 | `completion('vertex_ai/mistral-large@2407', messages)` |
+| mistral-nemo@latest | `completion('vertex_ai/mistral-nemo@latest', messages)` |
+| codestral@latest | `completion('vertex_ai/codestral@latest', messages)` |
+| codestral@@2405 | `completion('vertex_ai/codestral@2405', messages)` |
+
+#### Usage
+
+
+
+
+```python
+from litellm import completion
+import os
+
+os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
+
+model = "mistral-large@2407"
+
+vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
+vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
+
+response = completion(
+ model="vertex_ai/" + model,
+ messages=[{"role": "user", "content": "hi"}],
+ vertex_ai_project=vertex_ai_project,
+ vertex_ai_location=vertex_ai_location,
+)
+print("\nModel Response", response)
+```
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: vertex-mistral
+ litellm_params:
+ model: vertex_ai/mistral-large@2407
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+ - model_name: vertex-mistral
+ litellm_params:
+ model: vertex_ai/mistral-large@2407
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "vertex-mistral", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+
+#### Usage - Codestral FIM
+
+Call Codestral on VertexAI via the OpenAI [`/v1/completion`](https://platform.openai.com/docs/api-reference/completions/create) endpoint for FIM tasks.
+
+Note: You can also call Codestral via `/chat/completion`.
+
+
+
+
+```python
+from litellm import completion
+import os
+
+# os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
+# OR run `!gcloud auth print-access-token` in your terminal
+
+model = "codestral@2405"
+
+vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
+vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
+
+response = text_completion(
+ model="vertex_ai/" + model,
+ vertex_ai_project=vertex_ai_project,
+ vertex_ai_location=vertex_ai_location,
+ prompt="def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():",
+ suffix="return True", # optional
+ temperature=0, # optional
+ top_p=1, # optional
+ max_tokens=10, # optional
+ min_tokens=10, # optional
+ seed=10, # optional
+ stop=["return"], # optional
+)
+
+print("\nModel Response", response)
+```
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: vertex-codestral
+ litellm_params:
+ model: vertex_ai/codestral@2405
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+ - model_name: vertex-codestral
+ litellm_params:
+ model: vertex_ai/codestral@2405
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl -X POST 'http://0.0.0.0:4000/completions' \
+ -H 'Authorization: Bearer sk-1234' \
+ -H 'Content-Type: application/json' \
+ -d '{
+ "model": "vertex-codestral", # 👈 the 'model_name' in config
+ "prompt": "def is_odd(n): \n return n % 2 == 1 \ndef test_is_odd():",
+ "suffix":"return True", # optional
+ "temperature":0, # optional
+ "top_p":1, # optional
+ "max_tokens":10, # optional
+ "min_tokens":10, # optional
+ "seed":10, # optional
+ "stop":["return"], # optional
+ }'
+```
+
+
+
+
+
+## VertexAI AI21 Models
+
+| Model Name | Function Call |
+|------------------|--------------------------------------|
+| jamba-1.5-mini@001 | `completion(model='vertex_ai/jamba-1.5-mini@001', messages)` |
+| jamba-1.5-large@001 | `completion(model='vertex_ai/jamba-1.5-large@001', messages)` |
+
+#### Usage
+
+
+
+
+```python
+from litellm import completion
+import os
+
+os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = ""
+
+model = "meta/jamba-1.5-mini@001"
+
+vertex_ai_project = "your-vertex-project" # can also set this as os.environ["VERTEXAI_PROJECT"]
+vertex_ai_location = "your-vertex-location" # can also set this as os.environ["VERTEXAI_LOCATION"]
+
+response = completion(
+ model="vertex_ai/" + model,
+ messages=[{"role": "user", "content": "hi"}],
+ vertex_ai_project=vertex_ai_project,
+ vertex_ai_location=vertex_ai_location,
+)
+print("\nModel Response", response)
+```
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: jamba-1.5-mini
+ litellm_params:
+ model: vertex_ai/jamba-1.5-mini@001
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+ - model_name: jamba-1.5-large
+ litellm_params:
+ model: vertex_ai/jamba-1.5-large@001
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-west-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "jamba-1.5-large",
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+
+## Model Garden
+
+:::tip
+
+All OpenAI compatible models from Vertex Model Garden are supported.
+
+:::
+
+#### Using Model Garden
+
+**Almost all Vertex Model Garden models are OpenAI compatible.**
+
+
+
+
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `vertex_ai/openai/{MODEL_ID}` |
+| Vertex Documentation | [SDK for Deploy & OpenAI Chat Completions](https://github.com/GoogleCloudPlatform/generative-ai/blob/main/open-models/get_started_with_model_garden_sdk.ipynb), [Vertex Model Garden](https://cloud.google.com/model-garden?hl=en) |
+| Supported Operations | `/chat/completions`, `/embeddings` |
+
+
+
+
+```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/openai/",
+ messages=[{ "content": "Hello, how are you?","role": "user"}]
+)
+```
+
+
+
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: llama3-1-8b-instruct
+ litellm_params:
+ model: vertex_ai/openai/5464397967697903616
+ vertex_ai_project: "my-test-project"
+ vertex_ai_location: "us-east-1"
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "llama3-1-8b-instruct", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+
+
+
+
+
+
+
+
+```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/",
+ messages=[{ "content": "Hello, how are you?","role": "user"}]
+)
+```
+
+
+
+
diff --git a/docs/my-website/docs/proxy/cli_sso.md b/docs/my-website/docs/proxy/cli_sso.md
new file mode 100644
index 00000000000..f7669d6a25c
--- /dev/null
+++ b/docs/my-website/docs/proxy/cli_sso.md
@@ -0,0 +1,56 @@
+# CLI Authentication
+
+Use the litellm cli to authenticate to the LiteLLM Gateway. This is great if you're trying to give a large number of developers self-serve access to the LiteLLM Gateway.
+
+
+## Demo
+
+
+
+## Usage
+
+
+1. **Install the CLI**
+
+ If you have [uv](https://github.com/astral-sh/uv) installed, you can try this:
+
+ ```shell
+ uv tool install 'litellm[proxy]'
+ ```
+
+ If that works, you'll see something like this:
+
+ ```shell
+ ...
+ Installed 2 executables: litellm, litellm-proxy
+ ```
+
+ and now you can use the tool by just typing `litellm-proxy` in your terminal:
+
+ ```shell
+ litellm-proxy
+ ```
+
+2. **Set up environment variables**
+
+ ```bash
+ export LITELLM_PROXY_URL=http://localhost:4000
+ ```
+
+ *(Replace with your actual proxy URL)*
+
+3. **Login**
+
+ ```shell
+ litellm-proxy login
+ ```
+
+ This will open a browser window to authenticate. If you have connected LiteLLM Proxy to your SSO provider, you should be able to login with your SSO credentials. Once logged in, you can use the CLI to make requests to the LiteLLM Gateway.
+
+4. **Make a test request to view models**
+
+ ```shell
+ litellm-proxy models list
+ ```
+
+ This will list all the models available to you.
\ No newline at end of file
diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md
index 617b08ae0f7..4333b0afc16 100644
--- a/docs/my-website/docs/proxy/config_settings.md
+++ b/docs/my-website/docs/proxy/config_settings.md
@@ -319,6 +319,7 @@ router_settings:
| ATHINA_API_KEY | API key for Athina service
| ATHINA_BASE_URL | Base URL for Athina service (defaults to `https://log.athina.ai`)
| AUTH_STRATEGY | Strategy used for authentication (e.g., OAuth, API key)
+| ANTHROPIC_API_KEY | API key for Anthropic service
| AWS_ACCESS_KEY_ID | Access Key ID for AWS services
| AWS_PROFILE_NAME | AWS CLI profile name to be used
| AWS_REGION_NAME | Default AWS region for service interactions
@@ -415,6 +416,8 @@ router_settings:
| DEFAULT_REPLICATE_GPU_PRICE_PER_SECOND | Default price per second for Replicate GPU. Default is 0.001400
| DEFAULT_REPLICATE_POLLING_DELAY_SECONDS | Default delay in seconds for Replicate polling. Default is 1
| DEFAULT_REPLICATE_POLLING_RETRIES | Default number of retries for Replicate polling. Default is 5
+| DEFAULT_SQS_BATCH_SIZE | Default batch size for SQS logging. Default is 512
+| DEFAULT_SQS_FLUSH_INTERVAL_SECONDS | Default flush interval for SQS logging. Default is 10
| DEFAULT_S3_BATCH_SIZE | Default batch size for S3 logging. Default is 512
| DEFAULT_S3_FLUSH_INTERVAL_SECONDS | Default flush interval for S3 logging. Default is 10
| DEFAULT_SLACK_ALERTING_THRESHOLD | Default threshold for Slack alerting. Default is 300
@@ -431,6 +434,9 @@ router_settings:
| DOCS_URL | The path to the Swagger API documentation. **By default this is "/"**
| EMAIL_LOGO_URL | URL for the logo used in emails
| EMAIL_SUPPORT_CONTACT | Support contact email address
+| EMAIL_SIGNATURE | Custom HTML footer/signature for all emails. Can include HTML tags for formatting and links.
+| EMAIL_SUBJECT_INVITATION | Custom subject template for invitation emails.
+| EMAIL_SUBJECT_KEY_CREATED | Custom subject template for key creation emails.
| EXPERIMENTAL_MULTI_INSTANCE_RATE_LIMITING | Flag to enable new multi-instance rate limiting. **Default is False**
| FIREWORKS_AI_4_B | Size parameter for Fireworks AI 4B model. Default is 4
| FIREWORKS_AI_16_B | Size parameter for Fireworks AI 16B model. Default is 16
@@ -469,12 +475,16 @@ router_settings:
| GALILEO_PASSWORD | Password for Galileo authentication
| GALILEO_PROJECT_ID | Project ID for Galileo usage
| GALILEO_USERNAME | Username for Galileo authentication
+| GITHUB_COPILOT_TOKEN_DIR | Directory to store GitHub Copilot token for `github_copilot` llm provider
+| GITHUB_COPILOT_API_KEY_FILE | File to store GitHub Copilot API key for `github_copilot` llm provider
+| GITHUB_COPILOT_ACCESS_TOKEN_FILE | File to store GitHub Copilot access token for `github_copilot` llm provider
| GREENSCALE_API_KEY | API key for Greenscale service
| GREENSCALE_ENDPOINT | Endpoint URL for Greenscale service
| GOOGLE_APPLICATION_CREDENTIALS | Path to Google Cloud credentials JSON file
| GOOGLE_CLIENT_ID | Client ID for Google OAuth
| GOOGLE_CLIENT_SECRET | Client secret for Google OAuth
| GOOGLE_KMS_RESOURCE_NAME | Name of the resource in Google KMS
+| GUARDRAILS_AI_API_BASE | Base URL for Guardrails AI API
| HEALTH_CHECK_TIMEOUT_SECONDS | Timeout in seconds for health checks. Default is 60
| HF_API_BASE | Base URL for Hugging Face API
| HCP_VAULT_ADDR | Address for [Hashicorp Vault Secret Manager](../secret.md#hashicorp-vault)
@@ -513,6 +523,10 @@ router_settings:
| LANGSMITH_PROJECT | Project name for Langsmith integration
| LANGSMITH_SAMPLING_RATE | Sampling rate for Langsmith logging
| LANGTRACE_API_KEY | API key for Langtrace service
+| LASSO_API_BASE | Base URL for Lasso API
+| LASSO_API_KEY | API key for Lasso service
+| LASSO_USER_ID | User ID for Lasso service
+| LASSO_CONVERSATION_ID | Conversation ID for Lasso service
| LENGTH_OF_LITELLM_GENERATED_KEY | Length of keys generated by LiteLLM. Default is 16
| LITERAL_API_KEY | API key for Literal integration
| LITERAL_API_URL | API URL for Literal service
@@ -529,6 +543,7 @@ router_settings:
| LITELLM_LICENSE | License key for LiteLLM usage
| LITELLM_LOCAL_MODEL_COST_MAP | Local configuration for model cost mapping in LiteLLM
| LITELLM_LOG | Enable detailed logging for LiteLLM
+| LITELLM_MASTER_KEY | Master key for proxy authentication
| LITELLM_MODE | Operating mode for LiteLLM (e.g., production, development)
| LITELLM_RATE_LIMIT_WINDOW_SIZE | Rate limit window size for LiteLLM. Default is 60
| LITELLM_SALT_KEY | Salt key for encryption in LiteLLM
@@ -586,6 +601,8 @@ router_settings:
| OTEL_SERVICE_NAME | Service name identifier for OpenTelemetry
| OTEL_TRACER_NAME | Tracer name for OpenTelemetry tracing
| PAGERDUTY_API_KEY | API key for PagerDuty Alerting
+| PANW_PRISMA_AIRS_API_KEY | API key for PANW Prisma AIRS service
+| PANW_PRISMA_AIRS_API_BASE | Base URL for PANW Prisma AIRS service
| PHOENIX_API_KEY | API key for Arize Phoenix
| PHOENIX_COLLECTOR_ENDPOINT | API endpoint for Arize Phoenix
| PHOENIX_COLLECTOR_HTTP_ENDPOINT | API http endpoint for Arize Phoenix
@@ -603,7 +620,6 @@ router_settings:
| PROXY_BUDGET_RESCHEDULER_MAX_TIME | Maximum time in seconds to wait before checking database for budget resets. Default is 605
| PROXY_BUDGET_RESCHEDULER_MIN_TIME | Minimum time in seconds to wait before checking database for budget resets. Default is 597
| PROXY_LOGOUT_URL | URL for logging out of the proxy service
-| LITELLM_MASTER_KEY | Master key for proxy authentication
| QDRANT_API_BASE | Base URL for Qdrant API
| QDRANT_API_KEY | API key for Qdrant service
| QDRANT_SCALAR_QUANTILE | Scalar quantile for Qdrant operations. Default is 0.99
@@ -638,6 +654,7 @@ router_settings:
| SSL_CERTIFICATE | Path to the SSL certificate file
| SSL_SECURITY_LEVEL | [BETA] Security level for SSL/TLS connections. E.g. `DEFAULT@SECLEVEL=1`
| SSL_VERIFY | Flag to enable or disable SSL certificate verification
+| SSL_CERT_FILE | Path to the SSL certificate file for custom CA bundle
| SUPABASE_KEY | API key for Supabase service
| SUPABASE_URL | Base URL for Supabase instance
| STORE_MODEL_IN_DB | If true, enables storing model + credential information in the DB.
diff --git a/docs/my-website/docs/proxy/custom_root_ui.md b/docs/my-website/docs/proxy/custom_root_ui.md
index 1bab9431474..28ef57d81a4 100644
--- a/docs/my-website/docs/proxy/custom_root_ui.md
+++ b/docs/my-website/docs/proxy/custom_root_ui.md
@@ -12,6 +12,9 @@ Requires v1.72.3 or higher.
:::
+Limitations:
+- This does not work in [litellm non-root](./deploy#non-root---without-internet-connection) images, as it requires write access to the UI files.
+
## Usage
### 1. Set `SERVER_ROOT_PATH` in your .env
diff --git a/docs/my-website/docs/proxy/customers.md b/docs/my-website/docs/proxy/customers.md
index 2035b24f3a6..ac160d26542 100644
--- a/docs/my-website/docs/proxy/customers.md
+++ b/docs/my-website/docs/proxy/customers.md
@@ -2,7 +2,7 @@ import Image from '@theme/IdealImage';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# 🙋♂️ Customers / End-User Budgets
+# Customers / End-User Budgets
Track spend, set budgets for your customers.
@@ -136,7 +136,7 @@ Create / Update a customer with budget
curl -X POST 'http://0.0.0.0:4000/customer/new'
-H 'Authorization: Bearer sk-1234'
-H 'Content-Type: application/json'
- -D '{
+ -d '{
"user_id" : "my-customer-id",
"max_budget": "0", # 👈 CAN BE FLOAT
}'
diff --git a/docs/my-website/docs/proxy/deploy.md b/docs/my-website/docs/proxy/deploy.md
index 4503b0469a2..ddd88bb2904 100644
--- a/docs/my-website/docs/proxy/deploy.md
+++ b/docs/my-website/docs/proxy/deploy.md
@@ -237,6 +237,9 @@ spec:
containers:
- name: litellm
image: ghcr.io/berriai/litellm:main-stable # it is recommended to fix a version generally
+ args:
+ - "--config"
+ - "/app/proxy_server_config.yaml"
ports:
- containerPort: 4000
volumeMounts:
@@ -386,7 +389,8 @@ spec:
- "/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
+ mountPath: /app/proxy_config.yaml
+ subPath: config.yaml # Specify the field under data of the ConfigMap litellm-config
readOnly: true
livenessProbe:
httpGet:
diff --git a/docs/my-website/docs/proxy/email.md b/docs/my-website/docs/proxy/email.md
index 4eb35367dbe..9cd027da7f6 100644
--- a/docs/my-website/docs/proxy/email.md
+++ b/docs/my-website/docs/proxy/email.md
@@ -124,9 +124,7 @@ On the Create Key Modal, Select Advanced Settings > Set Send Email to True.
/>
-
-
-## Customizing Email Branding
+## Email Customization
:::info
@@ -134,13 +132,96 @@ Customizing Email Branding is an Enterprise Feature [Get in touch with us for a
:::
-LiteLLM allows you to customize the:
-- Logo on the Email
-- Email support contact
+LiteLLM allows you to customize various aspects of your email notifications. Below is a complete reference of all customizable fields:
-Set the following in your env to customize your emails
+| Field | Environment Variable | Type | Default Value | Example | Description |
+|-------|-------------------|------|---------------|---------|-------------|
+| Logo URL | `EMAIL_LOGO_URL` | string | LiteLLM logo | `"https://your-company.com/logo.png"` | Public URL to your company logo |
+| Support Contact | `EMAIL_SUPPORT_CONTACT` | string | support@berri.ai | `"support@your-company.com"` | Email address for user support |
+| Email Signature | `EMAIL_SIGNATURE` | string (HTML) | Standard LiteLLM footer | `"
Best regards, Your Team
Visit us
"` | HTML-formatted footer for all emails |
+| Invitation Subject | `EMAIL_SUBJECT_INVITATION` | string | "LiteLLM: New User Invitation" | `"Welcome to Your Company!"` | Subject line for invitation emails |
+| Key Creation Subject | `EMAIL_SUBJECT_KEY_CREATED` | string | "LiteLLM: API Key Created" | `"Your New API Key is Ready"` | Subject line for key creation emails |
-```shell
-EMAIL_LOGO_URL="https://litellm-listing.s3.amazonaws.com/litellm_logo.png" # public url to your logo
-EMAIL_SUPPORT_CONTACT="support@berri.ai" # Your company support email
+
+## HTML Support in Email Signature
+
+The `EMAIL_SIGNATURE` field supports HTML formatting for rich, branded email footers. Here's an example of what you can include:
+
+```html
+Best regards, The LiteLLM Team
+
+ Documentation |
+ GitHub
+
+
+ This is an automated message from LiteLLM Proxy
+
```
+
+Supported HTML features:
+- Text formatting (bold, italic, etc.)
+- Line breaks (` `)
+- Links (``)
+- Paragraphs (``)
+- Basic inline styling
+- Company information and social media links
+- Legal disclaimers or terms of service links
+
+## Environment Variables
+
+You can customize the following aspects of emails through environment variables:
+
+```bash
+# Email Branding
+EMAIL_LOGO_URL="https://your-company.com/logo.png" # Custom logo URL
+EMAIL_SUPPORT_CONTACT="support@your-company.com" # Support contact email
+EMAIL_SIGNATURE="
Best regards, Your Company Team
Visit our website
" # Custom HTML footer/signature
+
+# Email Subject Lines
+EMAIL_SUBJECT_INVITATION="Welcome to Your Company!" # Subject for invitation emails
+EMAIL_SUBJECT_KEY_CREATED="Your API Key is Ready" # Subject for key creation emails
+```
+
+## HTML Support in Email Signature
+
+The `EMAIL_SIGNATURE` environment variable supports HTML formatting, allowing you to create rich, branded email footers. You can include:
+
+- Text formatting (bold, italic, etc.)
+- Line breaks using ` `
+- Links using ` `
+- Paragraphs using ``
+- Company information and social media links
+- Legal disclaimers or terms of service links
+
+Example HTML signature:
+```html
+
Best regards, The LiteLLM Team
+
+ Documentation |
+ GitHub
+
+
+ This is an automated message from LiteLLM Proxy
+
+```
+
+## Default Templates
+
+If environment variables are not set, LiteLLM will use default templates:
+
+- Default logo: LiteLLM logo
+- Default support contact: support@berri.ai
+- Default signature: Standard LiteLLM footer
+- Default subjects: "LiteLLM: \{event_message\}" (replaced with actual event message)
+
+## Template Variables
+
+When setting custom email subjects, you can use template variables that will be replaced with actual values:
+
+```bash
+# Examples of template variable usage
+EMAIL_SUBJECT_INVITATION="Welcome to \{company_name\}!"
+EMAIL_SUBJECT_KEY_CREATED="Your \{company_name\} API Key"
+```
+
+The system will automatically replace `\{event_message\}` and other template variables with their actual values when sending emails.
diff --git a/docs/my-website/docs/proxy/guardrails.md b/docs/my-website/docs/proxy/guardrails.md
index 264f13b46fe..10cd5d52a57 100644
--- a/docs/my-website/docs/proxy/guardrails.md
+++ b/docs/my-website/docs/proxy/guardrails.md
@@ -216,7 +216,7 @@ If you need to switch `pii_masking` off for an API Key set `"permissions": {"pii
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"permissions": {"pii_masking": true}
}'
```
diff --git a/docs/my-website/docs/proxy/guardrails/aporia_api.md b/docs/my-website/docs/proxy/guardrails/aporia_api.md
index d45c34d47f9..8c5c1ec1947 100644
--- a/docs/my-website/docs/proxy/guardrails/aporia_api.md
+++ b/docs/my-website/docs/proxy/guardrails/aporia_api.md
@@ -155,7 +155,7 @@ Use this to control what guardrails run per project. In this tutorial we only wa
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
}
}'
diff --git a/docs/my-website/docs/proxy/guardrails/guardrails_ai.md b/docs/my-website/docs/proxy/guardrails/guardrails_ai.md
index 3f63273fc51..dafce5d0cb5 100644
--- a/docs/my-website/docs/proxy/guardrails/guardrails_ai.md
+++ b/docs/my-website/docs/proxy/guardrails/guardrails_ai.md
@@ -74,7 +74,7 @@ Use this to control what guardrails run per project. In this tutorial we only wa
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"guardrails": ["guardrails_ai-guard"]
}
}'
diff --git a/docs/my-website/docs/proxy/guardrails/lakera_ai.md b/docs/my-website/docs/proxy/guardrails/lakera_ai.md
index e66329dcb0c..81dd3d8a60d 100644
--- a/docs/my-website/docs/proxy/guardrails/lakera_ai.md
+++ b/docs/my-website/docs/proxy/guardrails/lakera_ai.md
@@ -126,3 +126,30 @@ curl -i http://localhost:4000/v1/chat/completions \
+
+
+## Supported Params
+
+```yaml
+guardrails:
+ - guardrail_name: "lakera-guard"
+ litellm_params:
+ guardrail: lakera_v2 # supported values: "aporia", "bedrock", "lakera"
+ mode: "during_call"
+ api_key: os.environ/LAKERA_API_KEY
+ api_base: os.environ/LAKERA_API_BASE
+ ### OPTIONAL ###
+ # project_id: Optional[str] = None,
+ # payload: Optional[bool] = True,
+ # breakdown: Optional[bool] = True,
+ # metadata: Optional[Dict] = None,
+ # dev_info: Optional[bool] = True,
+```
+
+- `api_base`: (Optional[str]) The base of the Lakera integration. Defaults to `https://api.lakera.ai`
+- `api_key`: (str) The API Key for the Lakera integration.
+- `project_id`: (Optional[str]) ID of the relevant project
+- `payload`: (Optional[bool]) When true the response will return a payload object containing any PII, profanity or custom detector regex matches detected, along with their location within the contents.
+- `breakdown`: (Optional[bool]) When true the response will return a breakdown list of the detectors that were run, as defined in the policy, and whether each of them detected something or not.
+- `metadata`: (Optional[Dict]) Metadata tags can be attached to screening requests as an object that can contain any arbitrary key-value pairs.
+- `dev_info`: (Optional[bool]) When true the response will return an object with developer information about the build of Lakera Guard.
diff --git a/docs/my-website/docs/proxy/guardrails/quick_start.md b/docs/my-website/docs/proxy/guardrails/quick_start.md
index 55cfa98d486..620d354a510 100644
--- a/docs/my-website/docs/proxy/guardrails/quick_start.md
+++ b/docs/my-website/docs/proxy/guardrails/quick_start.md
@@ -421,7 +421,7 @@ Use this to control what guardrails run per API Key. In this tutorial we only wa
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
}
}'
diff --git a/docs/my-website/docs/proxy/logging.md b/docs/my-website/docs/proxy/logging.md
index 7ec9080dfdd..e1926d776ea 100644
--- a/docs/my-website/docs/proxy/logging.md
+++ b/docs/my-website/docs/proxy/logging.md
@@ -9,6 +9,7 @@ Log Proxy input, output, and exceptions using:
- Langfuse
- OpenTelemetry
- GCS, s3, Azure (Blob) Buckets
+- AWS SQS
- Lunary
- MLflow
- Deepeval
@@ -1384,6 +1385,75 @@ litellm_settings:
On s3 bucket, you will see the object key as `my-test-path/my-team-alias/...`
+## AWS SQS
+
+
+| Property | Details |
+|----------|---------|
+| Description | Log LLM Input/Output to AWS SQS Queue |
+| AWS Docs on SQS | [AWS SQS](https://aws.amazon.com/sqs/) |
+| Fields Logged to SQS | LiteLLM [Standard Logging Payload is logged for each LLM call](../proxy/logging_spec) |
+
+
+Log LLM Logs to [AWS Simple Queue Service (SQS)](https://aws.amazon.com/sqs/)
+
+We will use the litellm `--config` to set
+
+- `litellm.callbacks = ["aws_sqs"]`
+
+This will log all successful LLM calls to AWS SQS Queue
+
+**Step 1** Set AWS Credentials in .env
+
+```shell
+AWS_ACCESS_KEY_ID = ""
+AWS_SECRET_ACCESS_KEY = ""
+AWS_REGION_NAME = ""
+```
+
+**Step 2**: Create a `config.yaml` file and set `litellm_settings`: `callbacks`
+
+```yaml
+model_list:
+ - model_name: gpt-4o
+ litellm_params:
+ model: gpt-4o
+litellm_settings:
+ callbacks: ["aws_sqs"]
+ aws_sqs_callback_params:
+ sqs_queue_url: https://sqs.us-west-2.amazonaws.com/123456789012/my-queue # AWS SQS Queue URL
+ sqs_region_name: us-west-2 # AWS Region Name for SQS
+ sqs_aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID # use os.environ/ to pass environment variables. This is AWS Access Key ID for SQS
+ sqs_aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY # AWS Secret Access Key for SQS
+ sqs_batch_size: 10 # [OPTIONAL] Number of messages to batch before sending (default: 10)
+ sqs_flush_interval: 30 # [OPTIONAL] Time in seconds to wait before flushing batch (default: 30)
+```
+
+**Step 3**: Start the proxy, make a test request
+
+Start proxy
+
+```shell
+litellm --config config.yaml --debug
+```
+
+Test Request
+
+```shell
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Content-Type: application/json' \
+ --data ' {
+ "model": "gpt-4o",
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ]
+ }'
+```
+
+
## Azure Blob Storage
Log LLM Logs to [Azure Data Lake Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/data-lake-storage-introduction)
diff --git a/docs/my-website/docs/proxy/managed_batches.md b/docs/my-website/docs/proxy/managed_batches.md
index 1b9b71c1779..431d313fc18 100644
--- a/docs/my-website/docs/proxy/managed_batches.md
+++ b/docs/my-website/docs/proxy/managed_batches.md
@@ -147,7 +147,7 @@ print(file_response.text)
```python showLineNumbers title="create_batch.py"
...
-client.batches.list(limit=10, extra_body={"target_model_names": "gpt-4o-batch"})
+client.batches.list(limit=10, extra_query={"target_model_names": "gpt-4o-batch"})
```
### [Coming Soon] Cancel a batch
diff --git a/docs/my-website/docs/proxy/management_cli.md b/docs/my-website/docs/proxy/management_cli.md
index 6593b88ba4f..9ecc2ae8a34 100644
--- a/docs/my-website/docs/proxy/management_cli.md
+++ b/docs/my-website/docs/proxy/management_cli.md
@@ -57,6 +57,42 @@ and more, as well as making chat and HTTP requests to the proxy server.
- If you see an error, check your environment variables and proxy server status.
+## Authentication using CLI
+
+You can use the CLI to authenticate to the LiteLLM Gateway. This is great if you're trying to give a large number of developers self-serve access to the LiteLLM Gateway.
+
+:::info
+
+For an indepth guide, see [CLI Authentication](./cli_sso).
+
+:::
+
+
+
+1. **Set up the proxy URL**
+
+ ```bash
+ export LITELLM_PROXY_URL=http://localhost:4000
+ ```
+
+ *(Replace with your actual proxy URL)*
+
+2. **Login**
+
+ ```bash
+ litellm-proxy login
+ ```
+
+ This will open a browser window to authenticate. If you have connected LiteLLM Proxy to your SSO provider, you can login with your SSO credentials. Once logged in, you can use the CLI to make requests to the LiteLLM Gateway.
+
+3. **Test your authentication**
+
+ ```bash
+ litellm-proxy models list
+ ```
+
+ This will list all the models available to you.
+
## Main Commands
### Models Management
diff --git a/docs/my-website/docs/proxy/prometheus.md b/docs/my-website/docs/proxy/prometheus.md
index d3fb6eca591..e4ef6f183c0 100644
--- a/docs/my-website/docs/proxy/prometheus.md
+++ b/docs/my-website/docs/proxy/prometheus.md
@@ -64,9 +64,9 @@ Use this for for tracking per [user, key, team, etc.](virtual_keys)
| Metric Name | Description |
|----------------------|--------------------------------------|
| `litellm_spend_metric` | Total Spend, per `"user", "key", "model", "team", "end-user"` |
-| `litellm_total_tokens` | input + output tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
-| `litellm_input_tokens` | input tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
-| `litellm_output_tokens` | output tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
+| `litellm_total_tokens_metric` | input + output tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
+| `litellm_input_tokens_metric` | input tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
+| `litellm_output_tokens_metric` | output tokens per `"end_user", "hashed_api_key", "api_key_alias", "requested_model", "team", "team_alias", "user", "model"` |
### Team - Budget
@@ -288,10 +288,11 @@ Control which labels are included for each metric to reduce cardinality:
litellm_settings:
callbacks: ["prometheus"]
prometheus_metrics_config:
- - group: "spend_and_tokens"
+ - group: "token_consumption"
metrics:
- - "litellm_spend_metric"
- - "litellm_total_tokens"
+ - "litellm_input_tokens_metric"
+ - "litellm_output_tokens_metric"
+ - "litellm_total_tokens_metric"
include_labels:
- "model"
- "team"
@@ -324,7 +325,6 @@ litellm_settings:
# Budget metrics with full label set
- group: "budget_tracking"
metrics:
- - "litellm_spend_metric"
- "litellm_remaining_team_budget_metric"
include_labels:
- "team"
@@ -385,7 +385,7 @@ Use these metrics to monitor the health of the DB Transaction Queue. Eg. Monitor
-## **🔥 LiteLLM Maintained Grafana Dashboards **
+## 🔥 LiteLLM Maintained Grafana Dashboards
Link to Grafana Dashboards maintained by LiteLLM
diff --git a/docs/my-website/docs/proxy/prompt_management.md b/docs/my-website/docs/proxy/prompt_management.md
index 8ea17425c82..fc35fc5ef38 100644
--- a/docs/my-website/docs/proxy/prompt_management.md
+++ b/docs/my-website/docs/proxy/prompt_management.md
@@ -210,6 +210,7 @@ These are the params you can pass to the `litellm.completion` function in SDK an
```
prompt_id: str # required
prompt_variables: Optional[dict] # optional
+prompt_version: Optional[int] # optional
langfuse_public_key: Optional[str] # optional
langfuse_secret: Optional[str] # optional
langfuse_secret_key: Optional[str] # optional
diff --git a/docs/my-website/docs/proxy/service_accounts.md b/docs/my-website/docs/proxy/service_accounts.md
index 5825af4cb8d..49fe0173b07 100644
--- a/docs/my-website/docs/proxy/service_accounts.md
+++ b/docs/my-website/docs/proxy/service_accounts.md
@@ -6,8 +6,27 @@ import Image from '@theme/IdealImage';
Use this if you want to create Virtual Keys that are not owned by a specific user but instead created for production projects
+Why use a service account key?
+ - Prevent key from being deleted when user is deleted.
+ - Apply team limits, not team member limits to key.
+
## Usage
+Use the `/key/service-account/generate` endpoint to generate a service account key.
+
+
+```bash
+curl -L -X POST 'http://localhost:4000/key/service-account/generate' \
+-H 'Authorization: Bearer sk-1234' \
+-H 'Content-Type: application/json' \
+-d '{
+ "team_id": "my-unique-team"
+}'
+```
+
+## Example - require `user` param for all service account requests
+
+
### 1. Set settings for Service Accounts
Set `service_account_settings` if you want to create settings that only apply to service account keys
diff --git a/docs/my-website/docs/proxy/team_budgets.md b/docs/my-website/docs/proxy/team_budgets.md
index 3942bfa504f..854d6edf304 100644
--- a/docs/my-website/docs/proxy/team_budgets.md
+++ b/docs/my-website/docs/proxy/team_budgets.md
@@ -2,7 +2,7 @@ import Image from '@theme/IdealImage';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# 💰 Setting Team Budgets
+# Setting Team Budgets
Track spend, set budgets for your Internal Team
@@ -318,7 +318,7 @@ curl -X POST 'http://0.0.0.0:4000/key/generate' \
curl -X POST 'http://0.0.0.0:4000/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: sk-...' \ # 👈 key from step 2.
- -D '{
+ -d '{
"model": "gpt-3.5-turbo",
"messages": [
{
diff --git a/docs/my-website/docs/proxy/team_logging.md b/docs/my-website/docs/proxy/team_logging.md
index 779a6516b49..bb35839bb25 100644
--- a/docs/my-website/docs/proxy/team_logging.md
+++ b/docs/my-website/docs/proxy/team_logging.md
@@ -4,52 +4,25 @@ import TabItem from '@theme/TabItem';
# Team/Key Based Logging
-Allow each key/team to use their own Langfuse Project / custom callbacks
+## Overview
-**This allows you to do the following**
-```
+Allow each key/team to use their own Langfuse Project / custom callbacks. This enables granular control over logging and compliance requirements.
+
+**Example Use Cases:**
+```showLineNumbers title="Team Based Logging"
Team 1 -> Logs to Langfuse Project 1
Team 2 -> Logs to Langfuse Project 2
Team 3 -> Disabled Logging (for GDPR compliance)
```
-## Team Based Logging
+## Supported Logging Integrations
+- `langfuse`
+- `gcs_bucket`
+- `langsmith`
+- `arize`
-
-### Setting Team Logging via `config.yaml`
-
-Turn on/off logging and caching for a specific team id.
-
-**Example:**
-
-This config would send langfuse logs to 2 different langfuse projects, based on the team id
-
-```yaml
-litellm_settings:
- default_team_settings:
- - team_id: "dbe2f686-a686-4896-864a-4c3924458709"
- success_callback: ["langfuse"]
- langfuse_public_key: os.environ/LANGFUSE_PUB_KEY_1 # Project 1
- langfuse_secret: os.environ/LANGFUSE_PRIVATE_KEY_1 # Project 1
- - team_id: "06ed1e01-3fa7-4b9e-95bc-f2e59b74f3a8"
- success_callback: ["langfuse"]
- langfuse_public_key: os.environ/LANGFUSE_PUB_KEY_2 # Project 2
- langfuse_secret: os.environ/LANGFUSE_SECRET_2 # Project 2
-```
-
-Now, when you [generate keys](./virtual_keys.md) for this team-id
-
-```bash
-curl -X POST 'http://0.0.0.0:4000/key/generate' \
--H 'Authorization: Bearer sk-1234' \
--H 'Content-Type: application/json' \
--d '{"team_id": "06ed1e01-3fa7-4b9e-95bc-f2e59b74f3a8"}'
-```
-
-All requests made with these keys will log data to their team-specific logging. -->
-
-## [BETA] Team Logging via API
+## [BETA] Team Logging
:::info
@@ -57,7 +30,54 @@ All requests made with these keys will log data to their team-specific logging.
:::
+### UI Usage
+1. Create a Team with Logging Settings
+
+Create a team called "AI Agents"
+
+
+
+
+
+2. Create a Key for the Team
+
+We will create a key for the team "AI Agents". The team logging settings will be used for all keys created for the team.
+
+
+
+
+
+
+3. Make a test LLM API Request
+
+Use the new key to make a test LLM API Request, we expect to see the logs on your logging provider configured in step 1.
+
+
+
+
+
+4. Check Logs on your Logging Provider
+
+Navigate to your configured logging provider and check if you received the logs from step 2.
+
+
+
+
+
+### API Usage
### Set Callbacks Per Team
#### 1. Set callback for team
@@ -189,6 +209,37 @@ curl -X GET 'http://localhost:4000/team/dbe2f686-a686-4896-864a-4c3924458709/cal
+## Team Logging - `config.yaml`
+
+Turn on/off logging and caching for a specific team id.
+
+**Example:**
+
+This config would send langfuse logs to 2 different langfuse projects, based on the team id
+
+```yaml
+litellm_settings:
+ default_team_settings:
+ - team_id: "dbe2f686-a686-4896-864a-4c3924458709"
+ success_callback: ["langfuse"]
+ langfuse_public_key: os.environ/LANGFUSE_PUB_KEY_1 # Project 1
+ langfuse_secret: os.environ/LANGFUSE_PRIVATE_KEY_1 # Project 1
+ - team_id: "06ed1e01-3fa7-4b9e-95bc-f2e59b74f3a8"
+ success_callback: ["langfuse"]
+ langfuse_public_key: os.environ/LANGFUSE_PUB_KEY_2 # Project 2
+ langfuse_secret: os.environ/LANGFUSE_SECRET_2 # Project 2
+```
+
+Now, when you [generate keys](./virtual_keys.md) for this team-id
+
+```bash
+curl -X POST 'http://0.0.0.0:4000/key/generate' \
+-H 'Authorization: Bearer sk-1234' \
+-H 'Content-Type: application/json' \
+-d '{"team_id": "06ed1e01-3fa7-4b9e-95bc-f2e59b74f3a8"}'
+```
+
+All requests made with these keys will log data to their team-specific logging.
## [BETA] Key Based Logging
@@ -201,11 +252,51 @@ Use the `/key/generate` or `/key/update` endpoints to add logging callbacks to a
:::
-### How key based logging works:
+**How key based logging works:**
- If **Key has no callbacks** configured, it will use the default callbacks specified in the config.yaml file
- If **Key has callbacks** configured, it will use the callbacks specified in the key
+
+### UI Usage
+
+1. Create a Key with Logging Settings
+
+When creating a key, you can configure the specific logging settings for the key. These logging settings will be used for all requests made with this key.
+
+
+
+
+
+2. Make a test LLM API Request
+
+Use the new key to make a test LLM API Request, we expect to see the logs on your logging provider configured in step 1.
+
+
+
+
+
+3. Check Logs on your Logging Provider
+
+Navigate to your configured logging provider and check if you received the logs from step 2.
+
+
+
+
+
+### API Usage
+
+
+
diff --git a/docs/my-website/docs/proxy/token_auth.md b/docs/my-website/docs/proxy/token_auth.md
index c562c7fb713..82d2266dd0d 100644
--- a/docs/my-website/docs/proxy/token_auth.md
+++ b/docs/my-website/docs/proxy/token_auth.md
@@ -501,6 +501,145 @@ curl -L -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
}'
```
+## [BETA] Sync User Roles and Teams with IDP
+
+Automatically sync user roles and team memberships from your Identity Provider (IDP) to LiteLLM's database. This ensures that user permissions and team memberships in LiteLLM stay in sync with your IDP.
+
+**Note:** This is in beta and might change unexpectedly.
+
+### Use Cases
+
+- **Role Synchronization**: Automatically update user roles in LiteLLM when they change in your IDP
+- **Team Membership Sync**: Keep team memberships in sync between your IDP and LiteLLM
+- **Centralized Access Management**: Manage all user permissions through your IDP while maintaining LiteLLM functionality
+
+### Setup
+
+#### 1. Configure JWT Role Mapping
+
+Map roles from your JWT token to LiteLLM user roles:
+
+```yaml
+general_settings:
+ enable_jwt_auth: True
+ litellm_jwtauth:
+ user_id_jwt_field: "sub"
+ team_ids_jwt_field: "groups"
+ roles_jwt_field: "roles"
+ user_id_upsert: true
+ sync_user_role_and_teams: true # 👈 Enable sync functionality
+ jwt_litellm_role_map: # 👈 Map JWT roles to LiteLLM roles
+ - jwt_role: "ADMIN"
+ litellm_role: "proxy_admin"
+ - jwt_role: "USER"
+ litellm_role: "internal_user"
+ - jwt_role: "VIEWER"
+ litellm_role: "internal_user"
+```
+
+#### 2. JWT Role Mapping Spec
+
+- `jwt_role`: The role name as it appears in your JWT token. Supports wildcard patterns using `fnmatch` (e.g., `"ADMIN_*"` matches `"ADMIN_READ"`, `"ADMIN_WRITE"`, etc.)
+- `litellm_role`: The corresponding LiteLLM user role
+
+**Supported LiteLLM Roles:**
+- `proxy_admin`: Full administrative access
+- `internal_user`: Standard user access
+- `internal_user_view_only`: Read-only access
+
+#### 3. Example JWT Token
+
+```json
+{
+ "sub": "user-123",
+ "roles": ["ADMIN"],
+ "groups": ["team-alpha", "team-beta"],
+ "iat": 1234567890,
+ "exp": 1234567890
+}
+```
+
+### How It Works
+
+When a user makes a request with a JWT token:
+
+1. **Role Sync**:
+ - LiteLLM checks if the user's role in the JWT matches their role in the database
+ - If different, the user's role is updated in LiteLLM's database
+ - Uses the `jwt_litellm_role_map` to convert JWT roles to LiteLLM roles
+
+2. **Team Membership Sync**:
+ - Compares team memberships from the JWT token with the user's current teams in LiteLLM
+ - Adds the user to new teams found in the JWT
+ - Removes the user from teams not present in the JWT
+
+3. **Database Updates**:
+ - Updates happen automatically during the authentication process
+ - No manual intervention required
+
+### Configuration Options
+
+```yaml
+general_settings:
+ enable_jwt_auth: True
+ litellm_jwtauth:
+ # Required fields
+ user_id_jwt_field: "sub"
+ team_ids_jwt_field: "groups"
+ roles_jwt_field: "roles"
+
+ # Sync configuration
+ sync_user_role_and_teams: true
+ user_id_upsert: true
+
+ # Role mapping
+ jwt_litellm_role_map:
+ - jwt_role: "AI_ADMIN_*" # Wildcard pattern
+ litellm_role: "proxy_admin"
+ - jwt_role: "AI_USER"
+ litellm_role: "internal_user"
+```
+
+### Important Notes
+
+- **Performance**: Sync operations happen during authentication, which may add slight latency
+- **Database Access**: Requires database access for user and team updates
+- **Team Creation**: Teams mentioned in JWT tokens must exist in LiteLLM before sync can assign users to them
+- **Wildcard Support**: JWT role patterns support wildcard matching using `fnmatch`
+
+### Testing the Sync Feature
+
+1. **Create a test user with initial role**:
+
+```bash
+curl -X POST 'http://0.0.0.0:4000/user/new' \
+-H 'Authorization: Bearer ' \
+-H 'Content-Type: application/json' \
+-d '{
+ "user_id": "user-123",
+ "user_role": "internal_user"
+}'
+```
+
+2. **Make a request with JWT containing different role**:
+
+```bash
+curl -X POST 'http://0.0.0.0:4000/v1/chat/completions' \
+-H 'Content-Type: application/json' \
+-H 'Authorization: Bearer ' \
+-d '{
+ "model": "claude-sonnet-4-20250514",
+ "messages": [{"role": "user", "content": "Hello"}]
+}'
+```
+
+3. **Verify the role was updated**:
+
+```bash
+curl -X GET 'http://0.0.0.0:4000/user/info?user_id=user-123' \
+-H 'Authorization: Bearer '
+```
+
## All JWT Params
[**See Code**](https://github.com/BerriAI/litellm/blob/b204f0c01c703317d812a1553363ab0cb989d5b6/litellm/proxy/_types.py#L95)
diff --git a/docs/my-website/docs/proxy/users.md b/docs/my-website/docs/proxy/users.md
index a665474f24a..c812dccb199 100644
--- a/docs/my-website/docs/proxy/users.md
+++ b/docs/my-website/docs/proxy/users.md
@@ -1,7 +1,7 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-# 💰 Budgets, Rate Limits
+# Budgets, Rate Limits
Requirements:
diff --git a/docs/my-website/docs/rerank.md b/docs/my-website/docs/rerank.md
index 171e7ae3255..c7c0b37a936 100644
--- a/docs/my-website/docs/rerank.md
+++ b/docs/my-website/docs/rerank.md
@@ -113,7 +113,7 @@ curl http://0.0.0.0:4000/rerank \
|-------------|--------------------|
| Cohere (v1 + v2 clients) | [Usage](#quick-start) |
| Together AI| [Usage](../docs/providers/togetherai) |
-| Azure AI| [Usage](../docs/providers/azure_ai) |
+| Azure AI| [Usage](../docs/providers/azure_ai#rerank-endpoint) |
| Jina AI| [Usage](../docs/providers/jina_ai) |
| AWS Bedrock| [Usage](../docs/providers/bedrock#rerank-api) |
| HuggingFace| [Usage](../docs/providers/huggingface_rerank) |
diff --git a/docs/my-website/docs/tutorials/litellm_proxy_aporia.md b/docs/my-website/docs/tutorials/litellm_proxy_aporia.md
index 143512f99c2..07eb36baa8b 100644
--- a/docs/my-website/docs/tutorials/litellm_proxy_aporia.md
+++ b/docs/my-website/docs/tutorials/litellm_proxy_aporia.md
@@ -150,7 +150,7 @@ Use this to control what guardrails run per project. In this tutorial we only wa
curl -X POST 'http://0.0.0.0:4000/key/generate' \
-H 'Authorization: Bearer sk-1234' \
-H 'Content-Type: application/json' \
- -D '{
+ -d '{
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
}
}'
diff --git a/docs/my-website/img/key_logging.png b/docs/my-website/img/key_logging.png
new file mode 100644
index 00000000000..195d052f0a2
Binary files /dev/null and b/docs/my-website/img/key_logging.png differ
diff --git a/docs/my-website/img/key_logging2.png b/docs/my-website/img/key_logging2.png
new file mode 100644
index 00000000000..1043681f508
Binary files /dev/null and b/docs/my-website/img/key_logging2.png differ
diff --git a/docs/my-website/img/key_logging_arize.png b/docs/my-website/img/key_logging_arize.png
new file mode 100644
index 00000000000..e94d451cc81
Binary files /dev/null and b/docs/my-website/img/key_logging_arize.png differ
diff --git a/docs/my-website/img/langfuse_otel.png b/docs/my-website/img/langfuse_otel.png
new file mode 100644
index 00000000000..a91e337f2c5
Binary files /dev/null and b/docs/my-website/img/langfuse_otel.png differ
diff --git a/docs/my-website/img/release_notes/team_key_logging.png b/docs/my-website/img/release_notes/team_key_logging.png
new file mode 100644
index 00000000000..d6b6c6a70b6
Binary files /dev/null and b/docs/my-website/img/release_notes/team_key_logging.png differ
diff --git a/docs/my-website/img/team_logging1.png b/docs/my-website/img/team_logging1.png
new file mode 100644
index 00000000000..be00048fb69
Binary files /dev/null and b/docs/my-website/img/team_logging1.png differ
diff --git a/docs/my-website/img/team_logging2.png b/docs/my-website/img/team_logging2.png
new file mode 100644
index 00000000000..f690a5b8022
Binary files /dev/null and b/docs/my-website/img/team_logging2.png differ
diff --git a/docs/my-website/img/team_logging3.png b/docs/my-website/img/team_logging3.png
new file mode 100644
index 00000000000..02c31d9c8d5
Binary files /dev/null and b/docs/my-website/img/team_logging3.png differ
diff --git a/docs/my-website/img/team_logging4.png b/docs/my-website/img/team_logging4.png
new file mode 100644
index 00000000000..e2c6feb0124
Binary files /dev/null and b/docs/my-website/img/team_logging4.png differ
diff --git a/docs/my-website/release_notes/v1.73.6-stable/index.md b/docs/my-website/release_notes/v1.73.6-stable/index.md
index 0ab719cca94..6f8e9a50087 100644
--- a/docs/my-website/release_notes/v1.73.6-stable/index.md
+++ b/docs/my-website/release_notes/v1.73.6-stable/index.md
@@ -1,5 +1,5 @@
---
-title: "[PRE-RELEASE] v1.73.6-stable"
+title: "v1.73.6-stable"
slug: "v1-73-6-stable"
date: 2025-06-28T10:00:00
authors:
@@ -20,18 +20,27 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-:::warning
-
-## Known Issues
-
-The `non-root` docker image has a known issue around the UI not loading. If you use the `non-root` docker image we recommend waiting before upgrading to this version. We will post a patch fix for this.
-
-:::
-
## Deploy this version
-This release is not out yet. The pre-release will be live on Sunday and the stable release will be live on Wednesday.
+
+
+``` showLineNumbers title="docker run litellm"
+docker run \
+-e STORE_MODEL_IN_DB=True \
+-p 4000:4000 \
+ghcr.io/berriai/litellm:v1.73.6-stable
+```
+
+
+
+
+``` showLineNumbers title="pip install litellm"
+pip install litellm==1.73.6.post1
+```
+
+
+
---
diff --git a/docs/my-website/release_notes/v1.74.0-stable/index.md b/docs/my-website/release_notes/v1.74.0-stable/index.md
new file mode 100644
index 00000000000..e3608710aac
--- /dev/null
+++ b/docs/my-website/release_notes/v1.74.0-stable/index.md
@@ -0,0 +1,354 @@
+---
+title: "[Pre-Release] v1.74.0"
+slug: "v1-74-0-stable"
+date: 2025-07-05T10:00:00
+authors:
+ - name: Krrish Dholakia
+ title: CEO, LiteLLM
+ url: https://www.linkedin.com/in/krish-d/
+ image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg
+ - 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
+
+hide_table_of_contents: false
+---
+
+import Image from '@theme/IdealImage';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+## Deploy this version
+
+
+
+
+``` showLineNumbers title="docker run litellm"
+docker run \
+-e STORE_MODEL_IN_DB=True \
+-p 4000:4000 \
+ghcr.io/berriai/litellm:v1.74.0.rc
+```
+
+
+
+
+``` showLineNumbers title="pip install litellm"
+pip install litellm==1.74.0.post1
+```
+
+
+
+
+---
+
+## Key Highlights
+
+
+### Azure Content Safety Guardrails
+
+### MCP Gateway: Segregate MCP tools
+
+MCP Server Segregation is now supported on LiteLLM. This means you can specify the `x-mcp-servers` header to specify which servers to list tools from. This is useful when you want to request tools from only a subset of configured servers — enabling curated toolsets and cleaner control.
+
+#### Usage
+
+
+
+
+```bash title="cURL Example with Server Segregation" showLineNumbers
+curl --location 'https://api.openai.com/v1/responses' \
+--header 'Content-Type: application/json' \
+--header "Authorization: Bearer $OPENAI_API_KEY" \
+--data '{
+ "model": "gpt-4o",
+ "tools": [
+ {
+ "type": "mcp",
+ "server_label": "litellm",
+ "server_url": "/mcp",
+ "require_approval": "never",
+ "headers": {
+ "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail"
+ }
+ }
+ ],
+ "input": "Run available tools",
+ "tool_choice": "required"
+}'
+```
+
+In this example, the request will only have access to tools from the "Zapier_Gmail" MCP server.
+
+
+
+
+
+```bash title="cURL Example with Server Segregation" showLineNumbers
+curl --location '/v1/responses' \
+--header 'Content-Type: application/json' \
+--header "Authorization: Bearer $LITELLM_API_KEY" \
+--data '{
+ "model": "gpt-4o",
+ "tools": [
+ {
+ "type": "mcp",
+ "server_label": "litellm",
+ "server_url": "/mcp",
+ "require_approval": "never",
+ "headers": {
+ "x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail,Server2"
+ }
+ }
+ ],
+ "input": "Run available tools",
+ "tool_choice": "required"
+}'
+```
+
+This configuration restricts the request to only use tools from the specified MCP servers.
+
+
+
+
+
+```json title="Cursor MCP Configuration with Server Segregation" showLineNumbers
+{
+ "mcpServers": {
+ "LiteLLM": {
+ "url": "/mcp",
+ "headers": {
+ "x-litellm-api-key": "Bearer $LITELLM_API_KEY",
+ "x-mcp-servers": "Zapier_Gmail,Server2"
+ }
+ }
+ }
+}
+```
+
+This configuration in Cursor IDE settings will limit tool access to only the specified MCP server.
+
+
+
+
+### Team / Key Based Logging on UI
+
+
+
+
+
+This release brings support for Proxy Admins to configure Team/Key Based Logging Settings on the UI. This allows routing LLM request/response logs to different Langfuse/Arize projects based on the team or key.
+
+For developers using LiteLLM, their logs are automatically routed to their specific Arize/Langfuse projects. On this release, we support the following integrations for key/team based logging:
+
+- `langfuse`
+- `arize`
+- `langsmith`
+
+
+
+
+### Python SDK: 2.3 Second Faster Import Times
+
+This release brings significant performance improvements to the Python SDK with 2.3 seconds faster import times. We've refactored the initialization process to reduce startup overhead, making LiteLLM more efficient for applications that need quick initialization. This is a major improvement for applications that need to initialize LiteLLM quickly.
+
+
+---
+
+## New Models / Updated Models
+
+#### Pricing / Context Window Updates
+
+| Provider | Model | Context Window | Input ($/1M tokens) | Output ($/1M tokens) | Type |
+| ----------- | -------------------------------------- | -------------- | ------------------- | -------------------- | ---- |
+| Watsonx | `watsonx/mistralai/mistral-large` | 131k | $3.00 | $10.00 | New |
+| Azure AI | `azure_ai/cohere-rerank-v3.5` | 4k | $2.00/1k queries | - | New (Rerank) |
+
+
+#### Features
+- **[🆕 GitHub Copilot](../../docs/providers/github_copilot)** - Use GitHub Copilot API with LiteLLM - [PR](https://github.com/BerriAI/litellm/pull/12325), [Get Started](../../docs/providers/github_copilot)
+- **[🆕 VertexAI DeepSeek](../../docs/providers/vertex)** - Add support for VertexAI DeepSeek models - [PR](https://github.com/BerriAI/litellm/pull/12312), [Get Started](../../docs/providers/vertex_partner#vertexai-deepseek)
+- **[Azure AI](../../docs/providers/azure_ai)**
+ - Add azure_ai cohere rerank v3.5 - [PR](https://github.com/BerriAI/litellm/pull/12283), [Get Started](../../docs/providers/azure_ai#rerank-endpoint)
+- **[Vertex AI](../../docs/providers/vertex)**
+ - Add size parameter support for image generation - [PR](https://github.com/BerriAI/litellm/pull/12292), [Get Started](../../docs/providers/vertex_image)
+- **[Custom LLM](../../docs/providers/custom_llm_server)**
+ - Pass through extra_ properties on "custom" llm provider - [PR](https://github.com/BerriAI/litellm/pull/12185)
+
+#### Bugs
+- **[Mistral](../../docs/providers/mistral)**
+ - Fix transform_response handling for empty string content - [PR](https://github.com/BerriAI/litellm/pull/12202)
+ - Turn Mistral to use llm_http_handler - [PR](https://github.com/BerriAI/litellm/pull/12245)
+- **[Gemini](../../docs/providers/gemini)**
+ - Fix tool call sequence - [PR](https://github.com/BerriAI/litellm/pull/11999)
+ - Fix custom api_base path preservation - [PR](https://github.com/BerriAI/litellm/pull/12215)
+- **[Anthropic](../../docs/providers/anthropic)**
+ - Fix user_id validation logic - [PR](https://github.com/BerriAI/litellm/pull/11432)
+- **[Bedrock](../../docs/providers/bedrock)**
+ - Support optional args for bedrock - [PR](https://github.com/BerriAI/litellm/pull/12287)
+- **[Ollama](../../docs/providers/ollama)**
+ - Fix default parameters for ollama-chat - [PR](https://github.com/BerriAI/litellm/pull/12201)
+- **[VLLM](../../docs/providers/vllm)**
+ - Add 'audio_url' message type support - [PR](https://github.com/BerriAI/litellm/pull/12270)
+
+---
+
+## LLM API Endpoints
+
+#### Features
+
+- **[/batches](../../docs/batches)**
+ - Support batch retrieve with target model Query Param - [PR](https://github.com/BerriAI/litellm/pull/12228)
+ - Anthropic completion bridge improvements - [PR](https://github.com/BerriAI/litellm/pull/12228)
+- **[/responses](../../docs/response_api)**
+ - Azure responses api bridge improvements - [PR](https://github.com/BerriAI/litellm/pull/12224)
+ - Fix responses api error handling - [PR](https://github.com/BerriAI/litellm/pull/12225)
+- **[/mcp (MCP Gateway)](../../docs/mcp)**
+ - Add MCP url masking on frontend - [PR](https://github.com/BerriAI/litellm/pull/12247)
+ - Add MCP servers header to scope - [PR](https://github.com/BerriAI/litellm/pull/12266)
+ - Litellm mcp tool prefix - [PR](https://github.com/BerriAI/litellm/pull/12289)
+ - Segregate MCP tools on connections using headers - [PR](https://github.com/BerriAI/litellm/pull/12296)
+ - Added changes to mcp url wrapping - [PR](https://github.com/BerriAI/litellm/pull/12207)
+
+
+#### Bugs
+- **[/v1/messages](../../docs/anthropic_unified)**
+ - Remove hardcoded model name on streaming - [PR](https://github.com/BerriAI/litellm/pull/12131)
+ - Support lowest latency routing - [PR](https://github.com/BerriAI/litellm/pull/12180)
+ - Non-anthropic models token usage returned - [PR](https://github.com/BerriAI/litellm/pull/12184)
+- **[/chat/completions](../../docs/providers/anthropic_unified)**
+ - Support Cursor IDE tool_choice format `{"type": "auto"}` - [PR](https://github.com/BerriAI/litellm/pull/12168)
+- **[/generateContent](../../docs/generate_content)**
+ - Allow passing litellm_params - [PR](https://github.com/BerriAI/litellm/pull/12177)
+ - Only pass supported params when using OpenAI models - [PR](https://github.com/BerriAI/litellm/pull/12297)
+ - Fix using gemini-cli with Vertex Anthropic Models - [PR](https://github.com/BerriAI/litellm/pull/12246)
+- **Streaming**
+ - Fix Error code: 307 for LlamaAPI Streaming Chat - [PR](https://github.com/BerriAI/litellm/pull/11946)
+ - Store finish reason even if is_finished - [PR](https://github.com/BerriAI/litellm/pull/12250)
+
+---
+
+## Spend Tracking / Budget Improvements
+
+#### Bugs
+ - Fix allow strings in calculate cost - [PR](https://github.com/BerriAI/litellm/pull/12200)
+ - VertexAI Anthropic streaming cost tracking with prompt caching fixes - [PR](https://github.com/BerriAI/litellm/pull/12188)
+
+---
+
+## Management Endpoints / UI
+
+#### Bugs
+- **Team Management**
+ - Prevent team model reset on model add - [PR](https://github.com/BerriAI/litellm/pull/12144)
+ - Return team-only models on /v2/model/info - [PR](https://github.com/BerriAI/litellm/pull/12144)
+ - Render team member budget correctly - [PR](https://github.com/BerriAI/litellm/pull/12144)
+- **UI Rendering**
+ - Fix rendering ui on non-root images - [PR](https://github.com/BerriAI/litellm/pull/12226)
+ - Correctly display 'Internal Viewer' user role - [PR](https://github.com/BerriAI/litellm/pull/12284)
+- **Configuration**
+ - Handle empty config.yaml - [PR](https://github.com/BerriAI/litellm/pull/12189)
+ - Fix gemini /models - replace models/ as expected - [PR](https://github.com/BerriAI/litellm/pull/12189)
+
+#### Features
+- **Team Management**
+ - Allow adding team specific logging callbacks - [PR](https://github.com/BerriAI/litellm/pull/12261)
+ - Add Arize Team Based Logging - [PR](https://github.com/BerriAI/litellm/pull/12264)
+ - Allow Viewing/Editing Team Based Callbacks - [PR](https://github.com/BerriAI/litellm/pull/12265)
+- **UI Improvements**
+ - Comma separated spend and budget display - [PR](https://github.com/BerriAI/litellm/pull/12317)
+ - Add logos to callback list - [PR](https://github.com/BerriAI/litellm/pull/12244)
+- **CLI**
+ - Add litellm-proxy cli login for starting to use litellm proxy - [PR](https://github.com/BerriAI/litellm/pull/12216)
+- **Email Templates**
+ - Customizable Email template - Subject and Signature - [PR](https://github.com/BerriAI/litellm/pull/12218)
+
+---
+
+## Logging / Guardrail Integrations
+
+#### Features
+- **[Azure Content Safety](../../docs/guardrails/azure_content_safety)**
+ - Add Azure Content Safety Guardrails to LiteLLM proxy - [PR](https://github.com/BerriAI/litellm/pull/12268)
+ - Add azure content safety guardrails to the UI - [PR](https://github.com/BerriAI/litellm/pull/12309)
+- **[DeepEval](../../docs/observability/deepeval_integration)**
+ - Fix DeepEval logging format for failure events - [PR](https://github.com/BerriAI/litellm/pull/12303)
+- **[Arize](../../docs/proxy/logging#arize)**
+ - Add Arize Team Based Logging - [PR](https://github.com/BerriAI/litellm/pull/12264)
+- **[Langfuse](../../docs/proxy/logging#langfuse)**
+ - Langfuse prompt_version support - [PR](https://github.com/BerriAI/litellm/pull/12301)
+- **[Sentry Integration](../../docs/observability/sentry)**
+ - Add sentry scrubbing - [PR](https://github.com/BerriAI/litellm/pull/12210)
+- **[AWS SQS Logging](../../docs/proxy/logging#aws-sqs)**
+ - New AWS SQS Logging Integration - [PR](https://github.com/BerriAI/litellm/pull/12176)
+- **[S3 Logger](../../docs/proxy/logging#s3-buckets)**
+ - Add failure logging support - [PR](https://github.com/BerriAI/litellm/pull/12299)
+- **[Prometheus Metrics](../../docs/proxy/prometheus)**
+ - Add better error validation for prometheus metrics and labels - [PR](https://github.com/BerriAI/litellm/pull/12182)
+
+#### Bugs
+- **Security**
+ - Ensure only LLM API route fails get logged on Langfuse - [PR](https://github.com/BerriAI/litellm/pull/12308)
+- **OpenMeter**
+ - Integration error handling fix - [PR](https://github.com/BerriAI/litellm/pull/12147)
+- **Message Redaction**
+ - Ensure message redaction works for responses API logging - [PR](https://github.com/BerriAI/litellm/pull/12291)
+- **Bedrock Guardrails**
+ - Fix bedrock guardrails post_call for streaming responses - [PR](https://github.com/BerriAI/litellm/pull/12252)
+---
+
+## Performance / Loadbalancing / Reliability improvements
+
+#### Features
+- **Python SDK**
+ - 2 second faster import times - [PR](https://github.com/BerriAI/litellm/pull/12135)
+ - Reduce python sdk import time by .3s - [PR](https://github.com/BerriAI/litellm/pull/12140)
+- **Error Handling**
+ - Add error handling for MCP tools not found or invalid server - [PR](https://github.com/BerriAI/litellm/pull/12223)
+- **SSL/TLS**
+ - Fix SSL certificate error - [PR](https://github.com/BerriAI/litellm/pull/12327)
+ - Fix custom ca bundle support in aiohttp transport - [PR](https://github.com/BerriAI/litellm/pull/12281)
+
+
+---
+
+## General Proxy Improvements
+
+- **Startup**
+ - Add new banner on startup - [PR](https://github.com/BerriAI/litellm/pull/12328)
+- **Dependencies**
+ - Update pydantic version - [PR](https://github.com/BerriAI/litellm/pull/12213)
+
+
+---
+
+## New Contributors
+* @wildcard made their first contribution in https://github.com/BerriAI/litellm/pull/12157
+* @colesmcintosh made their first contribution in https://github.com/BerriAI/litellm/pull/12168
+* @seyeong-han made their first contribution in https://github.com/BerriAI/litellm/pull/11946
+* @dinggh made their first contribution in https://github.com/BerriAI/litellm/pull/12162
+* @raz-alon made their first contribution in https://github.com/BerriAI/litellm/pull/11432
+* @tofarr made their first contribution in https://github.com/BerriAI/litellm/pull/12200
+* @szafranek made their first contribution in https://github.com/BerriAI/litellm/pull/12179
+* @SamBoyd made their first contribution in https://github.com/BerriAI/litellm/pull/12147
+* @lizzij made their first contribution in https://github.com/BerriAI/litellm/pull/12219
+* @cipri-tom made their first contribution in https://github.com/BerriAI/litellm/pull/12201
+* @zsimjee made their first contribution in https://github.com/BerriAI/litellm/pull/12185
+* @jroberts2600 made their first contribution in https://github.com/BerriAI/litellm/pull/12175
+* @njbrake made their first contribution in https://github.com/BerriAI/litellm/pull/12202
+* @NANDINI-star made their first contribution in https://github.com/BerriAI/litellm/pull/12244
+* @utsumi-fj made their first contribution in https://github.com/BerriAI/litellm/pull/12230
+* @dcieslak19973 made their first contribution in https://github.com/BerriAI/litellm/pull/12283
+* @hanouticelina made their first contribution in https://github.com/BerriAI/litellm/pull/12286
+* @lowjiansheng made their first contribution in https://github.com/BerriAI/litellm/pull/11999
+* @JoostvDoorn made their first contribution in https://github.com/BerriAI/litellm/pull/12281
+* @takashiishida made their first contribution in https://github.com/BerriAI/litellm/pull/12239
+
+## **[Git Diff](https://github.com/BerriAI/litellm/compare/v1.73.6-stable...v1.74.0-stable)**
+
diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js
index 60a7d1c971e..9ca01bd0079 100644
--- a/docs/my-website/sidebars.js
+++ b/docs/my-website/sidebars.js
@@ -142,6 +142,7 @@ const sidebars = {
"proxy/token_auth",
"proxy/service_accounts",
"proxy/access_control",
+ "proxy/cli_sso",
"proxy/custom_auth",
"proxy/ip_address",
"proxy/email",
@@ -255,6 +256,7 @@ const sidebars = {
"embedding/supported_embedding",
"anthropic_unified",
"mcp",
+ "generateContent",
{
type: "category",
label: "/images",
@@ -354,12 +356,12 @@ const sidebars = {
]
},
"providers/azure_ai",
- "providers/aiml",
{
type: "category",
label: "Vertex AI",
items: [
"providers/vertex",
+ "providers/vertex_partner",
"providers/vertex_image",
]
},
@@ -414,7 +416,6 @@ const sidebars = {
"providers/galadriel",
"providers/topaz",
"providers/groq",
- "providers/github",
"providers/deepseek",
"providers/elevenlabs",
"providers/fireworks_ai",
@@ -423,8 +424,11 @@ const sidebars = {
"providers/llamafile",
"providers/infinity",
"providers/xinference",
+ "providers/aiml",
"providers/cloudflare_workers",
"providers/deepinfra",
+ "providers/github",
+ "providers/github_copilot",
"providers/ai21",
"providers/nlp_cloud",
"providers/replicate",
diff --git a/enterprise/dist/litellm_enterprise-0.1.11-py3-none-any.whl b/enterprise/dist/litellm_enterprise-0.1.11-py3-none-any.whl
new file mode 100644
index 00000000000..3dece3053d2
Binary files /dev/null and b/enterprise/dist/litellm_enterprise-0.1.11-py3-none-any.whl differ
diff --git a/enterprise/dist/litellm_enterprise-0.1.11.tar.gz b/enterprise/dist/litellm_enterprise-0.1.11.tar.gz
new file mode 100644
index 00000000000..02b62c3ddac
Binary files /dev/null and b/enterprise/dist/litellm_enterprise-0.1.11.tar.gz differ
diff --git a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py
index a7c127cffff..086d1c7d156 100644
--- a/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py
+++ b/enterprise/litellm_enterprise/enterprise_callbacks/send_emails/base_email.py
@@ -29,6 +29,10 @@ from litellm.types.integrations.slack_alerting import LITELLM_LOGO_URL
class BaseEmailLogger(CustomLogger):
DEFAULT_LITELLM_EMAIL = "notifications@alerts.litellm.ai"
DEFAULT_SUPPORT_EMAIL = "support@berri.ai"
+ DEFAULT_SUBJECT_TEMPLATES = {
+ EmailEvent.new_user_invitation: "LiteLLM: {event_message}",
+ EmailEvent.virtual_key_created: "LiteLLM: {event_message}",
+ }
async def send_user_invitation_email(self, event: WebhookEvent):
"""
@@ -38,8 +42,8 @@ class BaseEmailLogger(CustomLogger):
email_event=EmailEvent.new_user_invitation,
user_id=event.user_id,
user_email=getattr(event, "user_email", None),
+ event_message=event.event_message,
)
- # Implement invitation email logic using email_params
verbose_proxy_logger.debug(
f"send_user_invitation_email_event: {json.dumps(event, indent=4, default=str)}"
@@ -50,13 +54,13 @@ class BaseEmailLogger(CustomLogger):
recipient_email=email_params.recipient_email,
base_url=email_params.base_url,
email_support_contact=email_params.support_contact,
- email_footer=EMAIL_FOOTER,
+ email_footer=email_params.signature,
)
await self.send_email(
from_email=self.DEFAULT_LITELLM_EMAIL,
to_email=[email_params.recipient_email],
- subject=f"LiteLLM: {event.event_message}",
+ subject=email_params.subject,
html_body=email_html_content,
)
@@ -68,11 +72,11 @@ class BaseEmailLogger(CustomLogger):
"""
Send email to user after creating key for the user
"""
-
email_params = await self._get_email_params(
user_id=send_key_created_email_event.user_id,
user_email=send_key_created_email_event.user_email,
email_event=EmailEvent.virtual_key_created,
+ event_message=send_key_created_email_event.event_message,
)
verbose_proxy_logger.debug(
@@ -86,13 +90,13 @@ class BaseEmailLogger(CustomLogger):
key_token=send_key_created_email_event.virtual_key,
base_url=email_params.base_url,
email_support_contact=email_params.support_contact,
- email_footer=EMAIL_FOOTER,
+ email_footer=email_params.signature,
)
await self.send_email(
from_email=self.DEFAULT_LITELLM_EMAIL,
to_email=[email_params.recipient_email],
- subject=f"LiteLLM: {send_key_created_email_event.event_message}",
+ subject=email_params.subject,
html_body=email_html_content,
)
pass
@@ -102,16 +106,63 @@ class BaseEmailLogger(CustomLogger):
email_event: EmailEvent,
user_id: Optional[str] = None,
user_email: Optional[str] = None,
+ event_message: Optional[str] = None,
) -> EmailParams:
"""
Get common email parameters used across different email sending methods
+ Args:
+ email_event: Type of email event
+ user_id: Optional user ID to look up email
+ user_email: Optional direct email address
+ event_message: Optional message to include in email subject
+
Returns:
- EmailParams object containing logo_url, support_contact, base_url, and recipient_email
+ EmailParams object containing logo_url, support_contact, base_url, recipient_email, subject, and signature
"""
- logo_url = os.getenv("EMAIL_LOGO_URL", None) or LITELLM_LOGO_URL
- support_contact = os.getenv("EMAIL_SUPPORT_CONTACT", self.DEFAULT_SUPPORT_EMAIL)
- base_url = os.getenv("PROXY_BASE_URL", "http://0.0.0.0:4000")
+ # Get email parameters with premium check for custom values
+ custom_logo = os.getenv("EMAIL_LOGO_URL", None)
+ custom_support = os.getenv("EMAIL_SUPPORT_CONTACT", None)
+ custom_signature = os.getenv("EMAIL_SIGNATURE", None)
+ custom_subject_invitation = os.getenv("EMAIL_SUBJECT_INVITATION", None)
+ custom_subject_key_created = os.getenv("EMAIL_SUBJECT_KEY_CREATED", None)
+
+ # Track which custom values were not applied
+ unused_custom_fields = []
+
+ # Function to safely get custom value or default
+ def get_custom_or_default(custom_value: Optional[str], default_value: str, field_name: str) -> str:
+ if custom_value is not None: # Only check premium if trying to use custom value
+ from litellm.proxy.proxy_server import premium_user
+ if premium_user is not True:
+ unused_custom_fields.append(field_name)
+ return default_value
+ return custom_value
+ return default_value
+
+ # Get parameters, falling back to defaults if custom values aren't allowed
+ logo_url = get_custom_or_default(custom_logo, LITELLM_LOGO_URL, "logo URL")
+ support_contact = get_custom_or_default(custom_support, self.DEFAULT_SUPPORT_EMAIL, "support contact")
+ base_url = os.getenv("PROXY_BASE_URL", "http://0.0.0.0:4000") # Not a premium feature
+ signature = get_custom_or_default(custom_signature, EMAIL_FOOTER, "email signature")
+
+ # Get custom subject template based on email event type
+ if email_event == EmailEvent.new_user_invitation:
+ subject_template = get_custom_or_default(
+ custom_subject_invitation,
+ self.DEFAULT_SUBJECT_TEMPLATES[EmailEvent.new_user_invitation],
+ "invitation subject template"
+ )
+ elif email_event == EmailEvent.virtual_key_created:
+ subject_template = get_custom_or_default(
+ custom_subject_key_created,
+ self.DEFAULT_SUBJECT_TEMPLATES[EmailEvent.virtual_key_created],
+ "key created subject template"
+ )
+ else:
+ subject_template = "LiteLLM: {event_message}"
+
+ subject = subject_template.format(event_message=event_message) if event_message else "LiteLLM Notification"
recipient_email: Optional[
str
@@ -127,11 +178,25 @@ class BaseEmailLogger(CustomLogger):
user_id=user_id, base_url=base_url
)
+ # If any custom fields were not applied, log a warning
+ if unused_custom_fields:
+ fields_str = ", ".join(unused_custom_fields)
+ warning_msg = (
+ f"Email sent with default values instead of custom values for: {fields_str}. "
+ "This is an Enterprise feature. To use custom email fields, please upgrade to LiteLLM Enterprise. "
+ "Schedule a meeting here: https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat"
+ )
+ verbose_proxy_logger.warning(
+ f"{warning_msg}"
+ )
+
return EmailParams(
logo_url=logo_url,
support_contact=support_contact,
base_url=base_url,
recipient_email=recipient_email,
+ subject=subject,
+ signature=signature,
)
def _format_key_budget(self, max_budget: Optional[float]) -> str:
diff --git a/enterprise/litellm_enterprise/types/enterprise_callbacks/send_emails.py b/enterprise/litellm_enterprise/types/enterprise_callbacks/send_emails.py
index 95bc7ff94e9..2d3c8adf2c6 100644
--- a/enterprise/litellm_enterprise/types/enterprise_callbacks/send_emails.py
+++ b/enterprise/litellm_enterprise/types/enterprise_callbacks/send_emails.py
@@ -5,19 +5,19 @@ from pydantic import BaseModel, Field
from litellm.proxy._types import WebhookEvent
-
class EmailParams(BaseModel):
logo_url: str
support_contact: str
base_url: str
recipient_email: str
+ subject: str
+ signature: str
class SendKeyCreatedEmailEvent(WebhookEvent):
virtual_key: str
"""
The virtual key that was created
-
this will be sk-123xxx, since we will be emailing this to the user to start using the key
"""
@@ -26,35 +26,25 @@ class EmailEvent(str, enum.Enum):
virtual_key_created = "Virtual Key Created"
new_user_invitation = "New User Invitation"
-
class EmailEventSettings(BaseModel):
event: EmailEvent
enabled: bool
-
-
class EmailEventSettingsUpdateRequest(BaseModel):
settings: List[EmailEventSettings]
-
-
class EmailEventSettingsResponse(BaseModel):
settings: List[EmailEventSettings]
-
-
class DefaultEmailSettings(BaseModel):
"""Default settings for email events"""
-
settings: Dict[EmailEvent, bool] = Field(
default_factory=lambda: {
EmailEvent.virtual_key_created: False, # Off by default
EmailEvent.new_user_invitation: True, # On by default
}
)
-
def to_dict(self) -> Dict[str, bool]:
"""Convert to dictionary with string keys for storage"""
return {event.value: enabled for event, enabled in self.settings.items()}
-
@classmethod
def get_defaults(cls) -> Dict[str, bool]:
"""Get the default settings as a dictionary with string keys"""
- return cls().to_dict()
+ return cls().to_dict()
\ No newline at end of file
diff --git a/enterprise/pyproject.toml b/enterprise/pyproject.toml
index 3095245c6c7..cad0bc7341d 100644
--- a/enterprise/pyproject.toml
+++ b/enterprise/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "litellm-enterprise"
-version = "0.1.10"
+version = "0.1.11"
description = "Package for LiteLLM Enterprise features"
authors = ["BerriAI"]
readme = "README.md"
@@ -22,7 +22,7 @@ requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.commitizen]
-version = "0.1.10"
+version = "0.1.11"
version_files = [
"pyproject.toml:version",
"../requirements.txt:litellm-enterprise==",
diff --git a/litellm/__init__.py b/litellm/__init__.py
index 7a9e677e74b..af723b99728 100644
--- a/litellm/__init__.py
+++ b/litellm/__init__.py
@@ -2,7 +2,7 @@
import warnings
warnings.filterwarnings("ignore", message=".*conflict with protected namespace.*")
-### INIT VARIABLES ############
+### INIT VARIABLES ################
import threading
import os
from typing import Callable, List, Optional, Dict, Union, Any, Literal, get_args
@@ -118,6 +118,7 @@ _custom_logger_compatible_callbacks_literal = Literal[
"smtp_email",
"deepeval",
"s3_v2",
+ "aws_sqs",
]
logged_real_time_event_types: Optional[Union[List[str], Literal["*"]]] = None
_known_custom_logger_compatible_callbacks: List = list(
@@ -214,6 +215,7 @@ use_litellm_proxy: bool = (
)
use_client: bool = False
ssl_verify: Union[str, bool] = True
+ssl_security_level: Optional[str] = None
ssl_certificate: Optional[str] = None
disable_streaming_logging: bool = False
disable_token_counter: bool = False
@@ -292,6 +294,7 @@ model_cost_map_url: str = (
suppress_debug_info = False
dynamodb_table_name: Optional[str] = None
s3_callback_params: Optional[Dict] = None
+aws_sqs_callback_params: Optional[Dict] = None
generic_logger_headers: Optional[Dict] = None
default_key_generate_params: Optional[Dict] = None
upperbound_key_generate_params: Optional[LiteLLM_UpperboundKeyGenerateParams] = None
@@ -1050,7 +1053,7 @@ from .llms.groq.chat.transformation import GroqChatConfig
from .llms.voyage.embedding.transformation import VoyageEmbeddingConfig
from .llms.infinity.embedding.transformation import InfinityEmbeddingConfig
from .llms.azure_ai.chat.transformation import AzureAIStudioConfig
-from .llms.mistral.mistral_chat_transformation import MistralConfig
+from .llms.mistral.chat.transformation import MistralConfig
from .llms.openai.responses.transformation import OpenAIResponsesAPIConfig
from .llms.azure.responses.transformation import AzureOpenAIResponsesAPIConfig
from .llms.openai.chat.o_series_transformation import (
@@ -1122,6 +1125,7 @@ from .llms.azure.chat.o_series_transformation import AzureOpenAIO1Config
from .llms.watsonx.completion.transformation import IBMWatsonXAIConfig
from .llms.watsonx.chat.transformation import IBMWatsonXChatConfig
from .llms.watsonx.embed.transformation import IBMWatsonXEmbeddingConfig
+from .llms.github_copilot.chat.transformation import GithubCopilotConfig
from .llms.nebius.chat.transformation import NebiusConfig
from .main import * # type: ignore
from .integrations import *
diff --git a/litellm/completion_extras/litellm_responses_transformation/handler.py b/litellm/completion_extras/litellm_responses_transformation/handler.py
index ea5e8b4c8dd..f2eeaf04554 100644
--- a/litellm/completion_extras/litellm_responses_transformation/handler.py
+++ b/litellm/completion_extras/litellm_responses_transformation/handler.py
@@ -75,9 +75,7 @@ class ResponsesToCompletionBridgeHandler:
custom_llm_provider=custom_llm_provider,
)
- def completion(
- self, *args, **kwargs
- ) -> Union[
+ def completion(self, *args, **kwargs) -> Union[
Coroutine[Any, Any, Union["ModelResponse", "CustomStreamWrapper"]],
"ModelResponse",
"CustomStreamWrapper",
@@ -106,6 +104,7 @@ class ResponsesToCompletionBridgeHandler:
litellm_params=litellm_params,
headers=headers,
litellm_logging_obj=logging_obj,
+ client=kwargs.get("client"),
)
result = responses(
diff --git a/litellm/completion_extras/litellm_responses_transformation/transformation.py b/litellm/completion_extras/litellm_responses_transformation/transformation.py
index 9ba42ffff58..7d10f6b2166 100644
--- a/litellm/completion_extras/litellm_responses_transformation/transformation.py
+++ b/litellm/completion_extras/litellm_responses_transformation/transformation.py
@@ -121,6 +121,7 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
litellm_params: dict,
headers: dict,
litellm_logging_obj: "LiteLLMLoggingObj",
+ client: Optional[Any] = None,
) -> dict:
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
@@ -186,6 +187,7 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
"input": input_items,
"litellm_logging_obj": litellm_logging_obj,
**litellm_params,
+ "client": client,
}
verbose_logger.debug(
diff --git a/litellm/constants.py b/litellm/constants.py
index 05e902065b9..98e9cf4ffc2 100644
--- a/litellm/constants.py
+++ b/litellm/constants.py
@@ -8,6 +8,12 @@ DEFAULT_S3_FLUSH_INTERVAL_SECONDS = int(
os.getenv("DEFAULT_S3_FLUSH_INTERVAL_SECONDS", 10)
)
DEFAULT_S3_BATCH_SIZE = int(os.getenv("DEFAULT_S3_BATCH_SIZE", 512))
+DEFAULT_SQS_FLUSH_INTERVAL_SECONDS = int(
+ os.getenv("DEFAULT_SQS_FLUSH_INTERVAL_SECONDS", 10)
+)
+DEFAULT_SQS_BATCH_SIZE = int(os.getenv("DEFAULT_SQS_BATCH_SIZE", 512))
+SQS_SEND_MESSAGE_ACTION = "SendMessage"
+SQS_API_VERSION = "2012-11-05"
DEFAULT_MAX_RETRIES = int(os.getenv("DEFAULT_MAX_RETRIES", 2))
DEFAULT_MAX_RECURSE_DEPTH = int(os.getenv("DEFAULT_MAX_RECURSE_DEPTH", 100))
DEFAULT_MAX_RECURSE_DEPTH_SENSITIVE_DATA_MASKER = int(
@@ -256,6 +262,7 @@ LITELLM_CHAT_PROVIDERS = [
"lm_studio",
"galadriel",
"gradient_ai",
+ "github_copilot", # GitHub Copilot Chat API
"novita",
"meta_llama",
"featherless_ai",
@@ -392,7 +399,6 @@ openai_compatible_endpoints: List = [
openai_compatible_providers: List = [
"anyscale",
- "mistral",
"groq",
"nvidia_nim",
"cerebras",
@@ -417,6 +423,7 @@ openai_compatible_providers: List = [
"llamafile",
"lm_studio",
"galadriel",
+ "github_copilot", # GitHub Copilot Chat API
"novita",
"meta_llama",
"featherless_ai",
@@ -713,6 +720,8 @@ MAXIMUM_TRACEBACK_LINES_TO_LOG = int(os.getenv("MAXIMUM_TRACEBACK_LINES_TO_LOG",
# Headers to control callbacks
X_LITELLM_DISABLE_CALLBACKS = "x-litellm-disable-callbacks"
+LITELLM_METADATA_FIELD = "litellm_metadata"
+OLD_LITELLM_METADATA_FIELD = "metadata"
########################### LiteLLM Proxy Specific Constants ###########################
########################################################################################
@@ -751,6 +760,10 @@ HEALTH_CHECK_TIMEOUT_SECONDS = int(
UI_SESSION_TOKEN_TEAM_ID = "litellm-dashboard"
LITELLM_PROXY_ADMIN_NAME = "default_user_id"
+########################### CLI SSO AUTHENTICATION CONSTANTS ###########################
+LITELLM_CLI_SOURCE_IDENTIFIER = "litellm-cli"
+LITELLM_CLI_SESSION_TOKEN_PREFIX = "litellm-session-token"
+
########################### DB CRON JOB NAMES ###########################
DB_SPEND_UPDATE_JOB_NAME = "db_spend_update_job"
PROMETHEUS_EMIT_BUDGET_METRICS_JOB_NAME = "prometheus_emit_budget_metrics"
@@ -792,3 +805,29 @@ SPECIAL_LITELLM_AUTH_TOKEN = ["ui-token"]
DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL = int(
os.getenv("DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL", 60)
)
+
+# Sentry Scrubbing Configuration
+SENTRY_DENYLIST = [
+ # API Keys and Tokens
+ "api_key", "token", "key", "secret", "password", "auth", "credential",
+ "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "AZURE_API_KEY", "COHERE_API_KEY",
+ "REPLICATE_API_KEY", "HUGGINGFACE_API_KEY", "TOGETHERAI_API_KEY",
+ "CLOUDFLARE_API_KEY", "BASETEN_KEY", "OPENROUTER_KEY", "DATAROBOT_API_TOKEN",
+ "FIREWORKS_API_KEY", "FIREWORKS_AI_API_KEY", "FIREWORKSAI_API_KEY",
+ # Database and Connection Strings
+ "database_url", "redis_url", "connection_string",
+ # Authentication and Security
+ "master_key", "LITELLM_MASTER_KEY", "auth_token", "jwt_token", "private_key",
+ "SLACK_WEBHOOK_URL", "webhook_url", "LANGFUSE_SECRET_KEY",
+ # Email Configuration
+ "SMTP_PASSWORD", "SMTP_USERNAME", "email_password",
+ # Cloud Provider Credentials
+ "aws_access_key", "aws_secret_key", "gcp_credentials",
+ "azure_credentials", "HCP_VAULT_TOKEN", "CIRCLE_OIDC_TOKEN",
+ # Proxy and Environment Settings
+ "proxy_url", "proxy_key", "environment_variables"
+]
+SENTRY_PII_DENYLIST = [
+ "user_id", "email", "phone", "address", "ip_address",
+ "SMTP_SENDER_EMAIL", "TEST_EMAIL_ADDRESS"
+]
\ No newline at end of file
diff --git a/litellm/experimental_mcp_client/client.py b/litellm/experimental_mcp_client/client.py
index af2cb171dad..e742cfcd18a 100644
--- a/litellm/experimental_mcp_client/client.py
+++ b/litellm/experimental_mcp_client/client.py
@@ -4,6 +4,7 @@ LiteLLM Proxy uses this MCP Client to connnect to other MCP servers.
import base64
from datetime import timedelta
from typing import List, Optional
+import asyncio
from mcp import ClientSession
from mcp.client.sse import sse_client
@@ -46,6 +47,7 @@ class MCPClient:
self._transport_ctx = None
self._transport = None
self._session_ctx = None
+ self._task: Optional[asyncio.Task] = None
# handle the basic auth value if provided
if auth_value:
@@ -56,8 +58,12 @@ class MCPClient:
Enable async context manager support.
Initializes the transport and session.
"""
- await self.connect()
- return self
+ try:
+ await self.connect()
+ return self
+ except Exception:
+ await self.disconnect()
+ raise
async def connect(self):
"""Initialize the transport and session."""
@@ -66,47 +72,63 @@ class MCPClient:
headers = self._get_auth_headers()
- if self.transport_type == MCPTransport.sse:
- self._transport_ctx = sse_client(
- url=self.server_url,
- timeout=self.timeout,
- headers=headers,
- )
- self._transport = await self._transport_ctx.__aenter__()
- self._session_ctx = ClientSession(self._transport[0], self._transport[1])
- self._session = await self._session_ctx.__aenter__()
- await self._session.initialize()
- else:
- self._transport_ctx = streamablehttp_client(
- url=self.server_url,
- timeout=timedelta(seconds=self.timeout),
- headers=headers,
- )
- self._transport = await self._transport_ctx.__aenter__()
- self._session_ctx = ClientSession(self._transport[0], self._transport[1])
- self._session = await self._session_ctx.__aenter__()
- await self._session.initialize()
+ try:
+ if self.transport_type == MCPTransport.sse:
+ self._transport_ctx = sse_client(
+ url=self.server_url,
+ timeout=self.timeout,
+ headers=headers,
+ )
+ self._transport = await self._transport_ctx.__aenter__()
+ self._session_ctx = ClientSession(self._transport[0], self._transport[1])
+ self._session = await self._session_ctx.__aenter__()
+ await self._session.initialize()
+ else:
+ self._transport_ctx = streamablehttp_client(
+ url=self.server_url,
+ timeout=timedelta(seconds=self.timeout),
+ headers=headers,
+ )
+ self._transport = await self._transport_ctx.__aenter__()
+ self._session_ctx = ClientSession(self._transport[0], self._transport[1])
+ self._session = await self._session_ctx.__aenter__()
+ await self._session.initialize()
+ except Exception:
+ await self.disconnect()
+ raise
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Cleanup when exiting context manager."""
- if self._session:
- await self._session_ctx.__aexit__(exc_type, exc_val, exc_tb) # type: ignore
- if self._transport_ctx:
- await self._transport_ctx.__aexit__(exc_type, exc_val, exc_tb)
+ await self.disconnect()
async def disconnect(self):
"""Clean up session and connections."""
+ if self._task and not self._task.done():
+ self._task.cancel()
+ try:
+ await self._task
+ except asyncio.CancelledError:
+ pass
+
if self._session:
try:
- # Ensure session is properly closed
- await self._session.close() # type: ignore
+ await self._session_ctx.__aexit__(None, None, None) # type: ignore
except Exception:
pass
self._session = None
+ self._session_ctx = None
+
+ if self._transport_ctx:
+ try:
+ await self._transport_ctx.__aexit__(None, None, None)
+ except Exception:
+ pass
+ self._transport_ctx = None
+ self._transport = None
if self._context:
try:
- await self._context.__aexit__(None, None, None) # type: ignore
+ await self._context.__aexit__(None, None, None) # type: ignore
except Exception:
pass
self._context = None
@@ -140,8 +162,15 @@ class MCPClient:
if self._session is None:
raise ValueError("Session is not initialized")
- result = await self._session.list_tools()
- return result.tools
+ try:
+ result = await self._session.list_tools()
+ return result.tools
+ except asyncio.CancelledError:
+ await self.disconnect()
+ raise
+ except Exception:
+ await self.disconnect()
+ raise
async def call_tool(
self, call_tool_request_params: MCPCallToolRequestParams
@@ -155,10 +184,17 @@ class MCPClient:
if self._session is None:
raise ValueError("Session is not initialized")
- tool_result = await self._session.call_tool(
- name=call_tool_request_params.name,
- arguments=call_tool_request_params.arguments,
- )
- return tool_result
+ try:
+ tool_result = await self._session.call_tool(
+ name=call_tool_request_params.name,
+ arguments=call_tool_request_params.arguments,
+ )
+ return tool_result
+ except asyncio.CancelledError:
+ await self.disconnect()
+ raise
+ except Exception:
+ await self.disconnect()
+ raise
diff --git a/litellm/google_genai/adapters/handler.py b/litellm/google_genai/adapters/handler.py
index 651a6413cad..ee7ddd0f2c6 100644
--- a/litellm/google_genai/adapters/handler.py
+++ b/litellm/google_genai/adapters/handler.py
@@ -1,6 +1,7 @@
from typing import Any, AsyncIterator, Coroutine, Dict, List, Optional, Union, cast
import litellm
+from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import ModelResponse
from .transformation import GoogleGenAIAdapter
@@ -18,6 +19,7 @@ class GenerateContentToCompletionHandler:
contents: Union[List[Dict[str, Any]], Dict[str, Any]],
config: Optional[Dict[str, Any]] = None,
stream: bool = False,
+ litellm_params: Optional[GenericLiteLLMParams] = None,
extra_kwargs: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""Prepare kwargs for litellm.completion/acompletion"""
@@ -27,6 +29,7 @@ class GenerateContentToCompletionHandler:
model=model,
contents=contents,
config=config,
+ litellm_params=litellm_params,
**(extra_kwargs or {})
)
@@ -41,6 +44,7 @@ class GenerateContentToCompletionHandler:
async def async_generate_content_handler(
model: str,
contents: Union[List[Dict[str, Any]], Dict[str, Any]],
+ litellm_params: GenericLiteLLMParams,
config: Optional[Dict[str, Any]] = None,
stream: bool = False,
**kwargs,
@@ -52,6 +56,7 @@ class GenerateContentToCompletionHandler:
contents=contents,
config=config,
stream=stream,
+ litellm_params=litellm_params,
extra_kwargs=kwargs,
)
@@ -82,6 +87,7 @@ class GenerateContentToCompletionHandler:
def generate_content_handler(
model: str,
contents: Union[List[Dict[str, Any]], Dict[str, Any]],
+ litellm_params: GenericLiteLLMParams,
config: Optional[Dict[str, Any]] = None,
stream: bool = False,
_is_async: bool = False,
@@ -95,6 +101,7 @@ class GenerateContentToCompletionHandler:
contents=contents,
config=config,
stream=stream,
+ litellm_params=litellm_params,
**kwargs,
)
@@ -103,6 +110,7 @@ class GenerateContentToCompletionHandler:
contents=contents,
config=config,
stream=stream,
+ litellm_params=litellm_params,
extra_kwargs=kwargs,
)
diff --git a/litellm/google_genai/adapters/transformation.py b/litellm/google_genai/adapters/transformation.py
index e80da47d5ea..f4c86f4673f 100644
--- a/litellm/google_genai/adapters/transformation.py
+++ b/litellm/google_genai/adapters/transformation.py
@@ -13,6 +13,7 @@ from litellm.types.llms.openai import (
ChatCompletionToolParam,
ChatCompletionUserMessage,
)
+from litellm.types.router import GenericLiteLLMParams
from litellm.types.utils import (
AdapterCompletionStreamWrapper,
Choices,
@@ -107,8 +108,9 @@ class GoogleGenAIAdapter:
model: str,
contents: Union[List[Dict[str, Any]], Dict[str, Any]],
config: Optional[Dict[str, Any]] = None,
+ litellm_params: Optional[GenericLiteLLMParams] = None,
**kwargs,
- ) -> ChatCompletionRequest:
+ ) -> Dict[str, Any]:
"""
Transform generate_content request to litellm completion format
@@ -119,7 +121,7 @@ class GoogleGenAIAdapter:
**kwargs: Additional parameters
Returns:
- ChatCompletionRequest in OpenAI format
+ Dict in OpenAI format
"""
# Normalize contents to list format
@@ -131,11 +133,11 @@ class GoogleGenAIAdapter:
# Transform contents to OpenAI messages format
messages = self._transform_contents_to_messages(contents_list)
- # Create base request
- completion_request: ChatCompletionRequest = ChatCompletionRequest(
- model=model,
- messages=messages,
- )
+ # Create base request as dict (which is compatible with ChatCompletionRequest)
+ completion_request: ChatCompletionRequest = {
+ "model": model,
+ "messages": messages,
+ }
#########################################################
# Supported OpenAI chat completion params
@@ -182,8 +184,40 @@ class GoogleGenAIAdapter:
)
if tool_choice:
completion_request["tool_choice"] = tool_choice
+
+ #########################################################
+ # forward any litellm specific params
+ #########################################################
+ completion_request_dict = dict(completion_request)
+ if litellm_params:
+ completion_request_dict = self._add_generic_litellm_params_to_request(
+ completion_request_dict=completion_request_dict,
+ litellm_params=litellm_params
+ )
- return completion_request
+ return completion_request_dict
+
+ def _add_generic_litellm_params_to_request(
+ self,
+ completion_request_dict: Dict[str, Any],
+ litellm_params: Optional[GenericLiteLLMParams] = None
+ ) -> dict:
+ """Add generic litellm params to request. e.g add api_base, api_key, api_version, etc.
+
+ Args:
+ completion_request_dict: Dict[str, Any]
+ litellm_params: GenericLiteLLMParams
+
+ Returns:
+ Dict[str, Any]
+ """
+ allowed_fields = GenericLiteLLMParams.model_fields.keys()
+ if litellm_params:
+ litellm_dict = litellm_params.model_dump(exclude_none=True)
+ for key, value in litellm_dict.items():
+ if key in allowed_fields:
+ completion_request_dict[key] = value
+ return completion_request_dict
def translate_completion_output_params_streaming(
self, completion_stream: Any
diff --git a/litellm/google_genai/main.py b/litellm/google_genai/main.py
index f1847057db9..2e80549f7a0 100644
--- a/litellm/google_genai/main.py
+++ b/litellm/google_genai/main.py
@@ -29,7 +29,7 @@ else:
GenerateContentConfigDict = Any
GenerateContentContentListUnionDict = Any
GenerateContentResponse = Any
-
+
####### ENVIRONMENT VARIABLES ###################
# Initialize any necessary instances or variables here
base_llm_http_handler = BaseLLMHTTPHandler()
@@ -38,6 +38,7 @@ base_llm_http_handler = BaseLLMHTTPHandler()
class GenerateContentSetupResult(BaseModel):
"""Internal Type - Result of setting up a generate content call"""
+
model: str
request_body: Dict[str, Any]
custom_llm_provider: str
@@ -53,7 +54,7 @@ class GenerateContentSetupResult(BaseModel):
class GenerateContentHelper:
"""Helper class for Google GenAI generate content operations"""
-
+
@staticmethod
def mock_generate_content_response(
mock_response: str = "This is a mock response from Google GenAI generate_content.",
@@ -63,20 +64,17 @@ class GenerateContentHelper:
"text": mock_response,
"candidates": [
{
- "content": {
- "parts": [{"text": mock_response}],
- "role": "model"
- },
+ "content": {"parts": [{"text": mock_response}], "role": "model"},
"finishReason": "STOP",
"index": 0,
- "safetyRatings": []
+ "safetyRatings": [],
}
],
"usageMetadata": {
"promptTokenCount": 10,
"candidatesTokenCount": 20,
- "totalTokenCount": 30
- }
+ "totalTokenCount": 30,
+ },
}
@staticmethod
@@ -86,11 +84,11 @@ class GenerateContentHelper:
config: Optional[GenerateContentConfigDict] = None,
custom_llm_provider: Optional[str] = None,
stream: bool = False,
- **kwargs
+ **kwargs,
) -> GenerateContentSetupResult:
"""
Common setup logic for generate_content calls
-
+
Args:
model: The model name
contents: The content to generate from
@@ -99,18 +97,24 @@ class GenerateContentHelper:
stream: Whether this is a streaming call
local_vars: Local variables from the calling function
**kwargs: Additional keyword arguments
-
+
Returns:
GenerateContentSetupResult containing all setup information
"""
- litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get("litellm_logging_obj")
+ litellm_logging_obj: Optional[LiteLLMLoggingObj] = kwargs.get(
+ "litellm_logging_obj"
+ )
litellm_call_id: Optional[str] = kwargs.get("litellm_call_id", None)
-
+
# get llm provider logic
litellm_params = GenericLiteLLMParams(**kwargs)
## MOCK RESPONSE LOGIC (only for non-streaming)
- if not stream and litellm_params.mock_response and isinstance(litellm_params.mock_response, str):
+ if (
+ not stream
+ and litellm_params.mock_response
+ and isinstance(litellm_params.mock_response, str)
+ ):
raise ValueError("Mock response should be handled by caller")
(
@@ -126,11 +130,11 @@ class GenerateContentHelper:
)
# get provider config
- generate_content_provider_config: Optional[BaseGoogleGenAIGenerateContentConfig] = (
- ProviderConfigManager.get_provider_google_genai_generate_content_config(
- model=model,
- provider=litellm.LlmProviders(custom_llm_provider),
- )
+ generate_content_provider_config: Optional[
+ BaseGoogleGenAIGenerateContentConfig
+ ] = ProviderConfigManager.get_provider_google_genai_generate_content_config(
+ model=model,
+ provider=litellm.LlmProviders(custom_llm_provider),
)
if generate_content_provider_config is None:
@@ -146,28 +150,31 @@ class GenerateContentHelper:
generate_content_config_dict=dict(config or {}),
litellm_params=litellm_params,
litellm_logging_obj=litellm_logging_obj,
- litellm_call_id=litellm_call_id
+ litellm_call_id=litellm_call_id,
)
-
#########################################################################################
# Construct request body
#########################################################################################
# Create Google Optional Params Config
- generate_content_config_dict = generate_content_provider_config.map_generate_content_optional_params(
- generate_content_config_dict=config or {},
- model=model,
+ generate_content_config_dict = (
+ generate_content_provider_config.map_generate_content_optional_params(
+ generate_content_config_dict=config or {},
+ model=model,
+ )
)
- request_body = generate_content_provider_config.transform_generate_content_request(
- model=model,
- contents=contents,
- generate_content_config_dict=generate_content_config_dict,
+ request_body = (
+ generate_content_provider_config.transform_generate_content_request(
+ model=model,
+ contents=contents,
+ generate_content_config_dict=generate_content_config_dict,
+ )
)
# Pre Call logging
if litellm_logging_obj is None:
raise ValueError("litellm_logging_obj is required, but got None")
-
+
litellm_logging_obj.update_environment_variables(
model=model,
optional_params=dict(generate_content_config_dict),
@@ -185,7 +192,7 @@ class GenerateContentHelper:
generate_content_config_dict=generate_content_config_dict,
litellm_params=litellm_params,
litellm_logging_obj=litellm_logging_obj,
- litellm_call_id=litellm_call_id
+ litellm_call_id=litellm_call_id,
)
@@ -202,7 +209,7 @@ async def agenerate_content(
timeout: Optional[Union[float, httpx.Timeout]] = None,
# LiteLLM specific params,
custom_llm_provider: Optional[str] = None,
- **kwargs
+ **kwargs,
) -> Any:
"""
Async: Generate content using Google GenAI
@@ -273,10 +280,12 @@ def generate_content(
local_vars = locals()
try:
_is_async = kwargs.pop("agenerate_content", False) is True
-
+
# Check for mock response first
litellm_params = GenericLiteLLMParams(**kwargs)
- if litellm_params.mock_response and isinstance(litellm_params.mock_response, str):
+ if litellm_params.mock_response and isinstance(
+ litellm_params.mock_response, str
+ ):
return GenerateContentHelper.mock_generate_content_response(
mock_response=litellm_params.mock_response
)
@@ -288,19 +297,20 @@ def generate_content(
config=config,
custom_llm_provider=custom_llm_provider,
stream=False,
- **kwargs
+ **kwargs,
)
# Check if we should use the adapter (when provider config is None)
if setup_result.generate_content_provider_config is None:
# Use the adapter to convert to completion format
return GenerateContentToCompletionHandler.generate_content_handler(
- model=setup_result.model,
+ model=model,
contents=contents, # type: ignore
config=setup_result.generate_content_config_dict,
stream=False,
_is_async=_is_async,
- **kwargs
+ litellm_params=setup_result.litellm_params,
+ **kwargs,
)
# Call the standard handler
@@ -345,7 +355,7 @@ async def agenerate_content_stream(
timeout: Optional[Union[float, httpx.Timeout]] = None,
# LiteLLM specific params,
custom_llm_provider: Optional[str] = None,
- **kwargs
+ **kwargs,
) -> Any:
"""
Async: Generate content using Google GenAI with streaming response
@@ -353,7 +363,7 @@ async def agenerate_content_stream(
local_vars = locals()
try:
kwargs["agenerate_content_stream"] = True
-
+
# get custom llm provider so we can use this for mapping exceptions
if custom_llm_provider is None:
_, custom_llm_provider, _, _ = litellm.get_llm_provider(
@@ -362,21 +372,24 @@ async def agenerate_content_stream(
# Setup the call
setup_result = GenerateContentHelper.setup_generate_content_call(
- model=model,
- contents=contents,
- config=config,
- custom_llm_provider=custom_llm_provider,
- stream=True,
- **kwargs
+ **{
+ "model": model,
+ "contents": contents,
+ "config": config,
+ "custom_llm_provider": custom_llm_provider,
+ "stream": True,
+ **kwargs,
+ }
)
# Check if we should use the adapter (when provider config is None)
if setup_result.generate_content_provider_config is None:
# Use the adapter to convert to completion format
return await GenerateContentToCompletionHandler.async_generate_content_handler(
- model=setup_result.model,
+ model=model,
contents=contents, # type: ignore
config=setup_result.generate_content_config_dict,
+ litellm_params=setup_result.litellm_params,
stream=True,
**kwargs
)
@@ -399,7 +412,7 @@ async def agenerate_content_stream(
stream=True,
litellm_metadata=kwargs.get("litellm_metadata", {}),
)
-
+
except Exception as e:
raise litellm.exception_type(
model=model,
@@ -440,19 +453,20 @@ def generate_content_stream(
config=config,
custom_llm_provider=custom_llm_provider,
stream=True,
- **kwargs
+ **kwargs,
)
# Check if we should use the adapter (when provider config is None)
if setup_result.generate_content_provider_config is None:
# Use the adapter to convert to completion format
return GenerateContentToCompletionHandler.generate_content_handler(
- model=setup_result.model,
+ model=model,
contents=contents, # type: ignore
config=setup_result.generate_content_config_dict,
stream=True,
_is_async=_is_async,
- **kwargs
+ litellm_params=setup_result.litellm_params,
+ **kwargs,
)
# Call the handler with streaming enabled (sync version)
@@ -481,4 +495,3 @@ def generate_content_stream(
completion_kwargs=local_vars,
extra_kwargs=kwargs,
)
-
diff --git a/litellm/integrations/anthropic_cache_control_hook.py b/litellm/integrations/anthropic_cache_control_hook.py
index 5c75e452ab7..29d9920da43 100644
--- a/litellm/integrations/anthropic_cache_control_hook.py
+++ b/litellm/integrations/anthropic_cache_control_hook.py
@@ -29,6 +29,7 @@ class AnthropicCacheControlHook(CustomPromptManagement):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Apply cache control directives based on specified injection points.
@@ -80,10 +81,10 @@ class AnthropicCacheControlHook(CustomPromptManagement):
# Case 1: Target by specific index
if targetted_index is not None:
if 0 <= targetted_index < len(messages):
- messages[
- targetted_index
- ] = AnthropicCacheControlHook._safe_insert_cache_control_in_message(
- messages[targetted_index], control
+ messages[targetted_index] = (
+ AnthropicCacheControlHook._safe_insert_cache_control_in_message(
+ messages[targetted_index], control
+ )
)
# Case 2: Target by role
elif targetted_role is not None:
diff --git a/litellm/integrations/arize/arize.py b/litellm/integrations/arize/arize.py
index 03b6966809c..1d78e4cc69c 100644
--- a/litellm/integrations/arize/arize.py
+++ b/litellm/integrations/arize/arize.py
@@ -12,6 +12,7 @@ from litellm.integrations.arize import _utils
from litellm.integrations.opentelemetry import OpenTelemetry
from litellm.types.integrations.arize import ArizeConfig
from litellm.types.services import ServiceLoggerPayload
+from litellm.types.utils import StandardCallbackDynamicParams
if TYPE_CHECKING:
from opentelemetry.trace import Span as _Span
@@ -102,3 +103,41 @@ class ArizeLogger(OpenTelemetry):
):
"""Arize is used mainly for LLM I/O tracing, sending Proxy Server Request adds bloat to arize logs"""
pass
+
+
+ def construct_dynamic_otel_headers(
+ self,
+ standard_callback_dynamic_params: StandardCallbackDynamicParams
+ ) -> Optional[dict]:
+ """
+ Construct dynamic Arize headers from standard callback dynamic params
+
+ This is used for team/key based logging.
+
+ Returns:
+ dict: A dictionary of dynamic Arize headers
+ """
+ dynamic_headers = {}
+
+ #########################################################
+ # `arize-space-id` handling
+ # the suggested param is `arize_space_key`
+ #########################################################
+ if standard_callback_dynamic_params.get("arize_space_id"):
+ dynamic_headers["arize-space-id"] = standard_callback_dynamic_params.get(
+ "arize_space_id"
+ )
+ if standard_callback_dynamic_params.get("arize_space_key"):
+ dynamic_headers["arize-space-id"] = standard_callback_dynamic_params.get(
+ "arize_space_key"
+ )
+
+ #########################################################
+ # `api_key` handling
+ #########################################################
+ if standard_callback_dynamic_params.get("arize_api_key"):
+ dynamic_headers["api_key"] = standard_callback_dynamic_params.get(
+ "arize_api_key"
+ )
+
+ return dynamic_headers
diff --git a/litellm/integrations/custom_guardrail.py b/litellm/integrations/custom_guardrail.py
index a82eed8eb8f..f858fcd10c8 100644
--- a/litellm/integrations/custom_guardrail.py
+++ b/litellm/integrations/custom_guardrail.py
@@ -1,5 +1,5 @@
from datetime import datetime
-from typing import Dict, List, Literal, Optional, Union
+from typing import Dict, List, Literal, Optional, Type, Union
from litellm._logging import verbose_logger
from litellm.integrations.custom_logger import CustomLogger
@@ -9,6 +9,7 @@ from litellm.types.guardrails import (
LitellmParams,
PiiEntityType,
)
+from litellm.types.proxy.guardrails.guardrail_hooks.base import GuardrailConfigModel
from litellm.types.utils import StandardLoggingGuardrailInformation
@@ -46,19 +47,34 @@ class CustomGuardrail(CustomLogger):
self.mask_response_content: bool = mask_response_content
if supported_event_hooks:
+
## validate event_hook is in supported_event_hooks
self._validate_event_hook(event_hook, supported_event_hooks)
super().__init__(**kwargs)
+ @staticmethod
+ def get_config_model() -> Optional[Type["GuardrailConfigModel"]]:
+ """
+ Returns the config model for the guardrail
+
+ This is used to render the config model in the UI.
+ """
+ return None
+
def _validate_event_hook(
self,
event_hook: Optional[Union[GuardrailEventHooks, List[GuardrailEventHooks]]],
supported_event_hooks: List[GuardrailEventHooks],
) -> None:
+
if event_hook is None:
return
+ if isinstance(event_hook, str):
+ event_hook = GuardrailEventHooks(event_hook)
if isinstance(event_hook, list):
for hook in event_hook:
+ if isinstance(hook, str):
+ hook = GuardrailEventHooks(hook)
if hook not in supported_event_hooks:
raise ValueError(
f"Event hook {hook} is not in the supported event hooks {supported_event_hooks}"
@@ -86,10 +102,13 @@ class CustomGuardrail(CustomLogger):
for _guardrail in requested_guardrails:
if isinstance(_guardrail, dict):
if self.guardrail_name in _guardrail:
+
return True
elif isinstance(_guardrail, str):
if self.guardrail_name == _guardrail:
+
return True
+
return False
def should_run_guardrail(self, data, event_type: GuardrailEventHooks) -> bool:
@@ -336,14 +355,11 @@ def log_guardrail_information(func):
import asyncio
import functools
- start_time = datetime.now()
-
@functools.wraps(func)
async def async_wrapper(*args, **kwargs):
+ start_time = datetime.now() # Move start_time inside the wrapper
self: CustomGuardrail = args[0]
- request_data: Optional[dict] = (
- kwargs.get("data") or kwargs.get("request_data") or {}
- )
+ request_data: dict = kwargs.get("data") or kwargs.get("request_data") or {}
try:
response = await func(*args, **kwargs)
return self._process_response(
@@ -364,10 +380,9 @@ def log_guardrail_information(func):
@functools.wraps(func)
def sync_wrapper(*args, **kwargs):
+ start_time = datetime.now() # Move start_time inside the wrapper
self: CustomGuardrail = args[0]
- request_data: Optional[dict] = (
- kwargs.get("data") or kwargs.get("request_data") or {}
- )
+ request_data: dict = kwargs.get("data") or kwargs.get("request_data") or {}
try:
response = func(*args, **kwargs)
return self._process_response(
diff --git a/litellm/integrations/custom_logger.py b/litellm/integrations/custom_logger.py
index 1cbcd360ce9..a9cbc65e6f4 100644
--- a/litellm/integrations/custom_logger.py
+++ b/litellm/integrations/custom_logger.py
@@ -89,6 +89,7 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
litellm_logging_obj: LiteLLMLoggingObj,
tools: Optional[List[Dict]] = None,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Returns:
@@ -107,6 +108,7 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Returns:
@@ -408,3 +410,20 @@ class CustomLogger: # https://docs.litellm.ai/docs/observability/custom_callbac
if len(text) > max_length
else text
)
+
+ def _select_metadata_field(
+ self, request_kwargs: Optional[Dict] = None
+ ) -> Optional[str]:
+ """
+ Select the metadata field to use for logging
+
+ 1. If `litellm_metadata` is in the request kwargs, use it
+ 2. Otherwise, use `metadata`
+ """
+ from litellm.constants import LITELLM_METADATA_FIELD, OLD_LITELLM_METADATA_FIELD
+
+ if request_kwargs is None:
+ return None
+ if LITELLM_METADATA_FIELD in request_kwargs:
+ return LITELLM_METADATA_FIELD
+ return OLD_LITELLM_METADATA_FIELD
diff --git a/litellm/integrations/custom_prompt_management.py b/litellm/integrations/custom_prompt_management.py
index 061aadc3c05..86cd1dc9f75 100644
--- a/litellm/integrations/custom_prompt_management.py
+++ b/litellm/integrations/custom_prompt_management.py
@@ -19,6 +19,7 @@ class CustomPromptManagement(CustomLogger, PromptManagementBase):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Returns:
@@ -45,6 +46,7 @@ class CustomPromptManagement(CustomLogger, PromptManagementBase):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> PromptManagementClient:
raise NotImplementedError(
"Custom prompt management does not support compile prompt helper"
diff --git a/litellm/integrations/datadog/datadog.py b/litellm/integrations/datadog/datadog.py
index fb6fee6dc6a..cd78ce4a7a1 100644
--- a/litellm/integrations/datadog/datadog.py
+++ b/litellm/integrations/datadog/datadog.py
@@ -576,4 +576,4 @@ class DataDogLogger(
start_time_utc: Optional[datetimeObj],
end_time_utc: Optional[datetimeObj],
) -> Optional[dict]:
- pass
+ pass
\ No newline at end of file
diff --git a/litellm/integrations/deepeval/deepeval.py b/litellm/integrations/deepeval/deepeval.py
index a94e02109ec..f548ff50d73 100644
--- a/litellm/integrations/deepeval/deepeval.py
+++ b/litellm/integrations/deepeval/deepeval.py
@@ -100,7 +100,7 @@ class DeepEvalLogger(CustomLogger):
except Exception as e:
raise e
verbose_logger.debug(
- "DeepEvalLogger: sync_log_failure_event: Api response", response
+ "DeepEvalLogger: sync_log_failure_event: Api response %s", response
)
async def _async_event_handler(
@@ -116,7 +116,7 @@ class DeepEvalLogger(CustomLogger):
)
verbose_logger.debug(
- "DeepEvalLogger: async_event_handler: Api response", response
+ "DeepEvalLogger: async_event_handler: Api response %s", response
)
def _create_base_api_span(
diff --git a/litellm/integrations/humanloop.py b/litellm/integrations/humanloop.py
index c62ab1110ff..9f43d806266 100644
--- a/litellm/integrations/humanloop.py
+++ b/litellm/integrations/humanloop.py
@@ -156,7 +156,12 @@ class HumanloopLogger(CustomLogger):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
- ) -> Tuple[str, List[AllMessageValues], dict,]:
+ prompt_version: Optional[int] = None,
+ ) -> 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_otel.py b/litellm/integrations/langfuse/langfuse_otel.py
index 6d7f927c3ef..7fc222ff6c6 100644
--- a/litellm/integrations/langfuse/langfuse_otel.py
+++ b/litellm/integrations/langfuse/langfuse_otel.py
@@ -1,6 +1,7 @@
import base64
import os
from typing import TYPE_CHECKING, Any, Union
+from urllib.parse import quote
from litellm._logging import verbose_logger
from litellm.integrations.arize import _utils
@@ -9,10 +10,11 @@ from litellm.types.integrations.langfuse_otel import LangfuseOtelConfig
if TYPE_CHECKING:
from opentelemetry.trace import Span as _Span
+ from litellm.integrations.opentelemetry import (
+ OpenTelemetryConfig as _OpenTelemetryConfig,
+ )
from litellm.types.integrations.arize import Protocol as _Protocol
- from litellm.integrations.opentelemetry import OpenTelemetryConfig as _OpenTelemetryConfig
-
Protocol = _Protocol
OpenTelemetryConfig = _OpenTelemetryConfig
Span = Union[_Span, Any]
@@ -54,7 +56,7 @@ class LangfuseOtelLogger:
"""
public_key = os.environ.get("LANGFUSE_PUBLIC_KEY", None)
secret_key = os.environ.get("LANGFUSE_SECRET_KEY", None)
-
+
if not public_key or not secret_key:
raise ValueError(
"LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY must be set for Langfuse OpenTelemetry integration."
@@ -62,7 +64,7 @@ class LangfuseOtelLogger:
# Determine endpoint - default to US cloud
langfuse_host = os.environ.get("LANGFUSE_HOST", None)
-
+
if langfuse_host:
# If LANGFUSE_HOST is provided, construct OTEL endpoint from it
if not langfuse_host.startswith("http"):
@@ -77,13 +79,13 @@ class LangfuseOtelLogger:
# Create Basic Auth header
auth_string = f"{public_key}:{secret_key}"
auth_header = base64.b64encode(auth_string.encode()).decode()
- otlp_auth_headers = f"Authorization=Basic {auth_header}"
+ # URL encode the entire header value as required by OpenTelemetry specification
+ otlp_auth_headers = f"Authorization={quote(f'Basic {auth_header}')}"
# Set standard OTEL environment variables
os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"] = endpoint
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = otlp_auth_headers
return LangfuseOtelConfig(
- otlp_auth_headers=otlp_auth_headers,
- protocol="otlp_http"
- )
\ No newline at end of file
+ otlp_auth_headers=otlp_auth_headers, protocol="otlp_http"
+ )
diff --git a/litellm/integrations/langfuse/langfuse_prompt_management.py b/litellm/integrations/langfuse/langfuse_prompt_management.py
index 8fe9cb63dea..58698ef35a5 100644
--- a/litellm/integrations/langfuse/langfuse_prompt_management.py
+++ b/litellm/integrations/langfuse/langfuse_prompt_management.py
@@ -134,8 +134,14 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
langfuse_prompt_id: str,
langfuse_client: LangfuseClass,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> PROMPT_CLIENT:
- return langfuse_client.get_prompt(langfuse_prompt_id, label=prompt_label)
+
+ prompt_client = langfuse_client.get_prompt(
+ langfuse_prompt_id, label=prompt_label, version=prompt_version
+ )
+
+ return prompt_client
def _compile_prompt(
self,
@@ -180,7 +186,12 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
litellm_logging_obj: LiteLLMLoggingObj,
tools: Optional[List[Dict]] = None,
prompt_label: Optional[str] = None,
- ) -> Tuple[str, List[AllMessageValues], dict,]:
+ prompt_version: Optional[int] = None,
+ ) -> Tuple[
+ str,
+ List[AllMessageValues],
+ dict,
+ ]:
return self.get_chat_completion_prompt(
model,
messages,
@@ -189,6 +200,7 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
prompt_variables,
dynamic_callback_params,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
def should_run_prompt_management(
@@ -203,7 +215,8 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
langfuse_host=dynamic_callback_params.get("langfuse_host"),
)
langfuse_prompt_client = self._get_prompt_from_id(
- langfuse_prompt_id=prompt_id, langfuse_client=langfuse_client
+ langfuse_prompt_id=prompt_id,
+ langfuse_client=langfuse_client,
)
return langfuse_prompt_client is not None
@@ -213,6 +226,7 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> PromptManagementClient:
langfuse_client = langfuse_client_init(
langfuse_public_key=dynamic_callback_params.get("langfuse_public_key"),
@@ -224,6 +238,7 @@ class LangfusePromptManagement(LangFuseLogger, PromptManagementBase, CustomLogge
langfuse_prompt_id=prompt_id,
langfuse_client=langfuse_client,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
## SET PROMPT
diff --git a/litellm/integrations/openmeter.py b/litellm/integrations/openmeter.py
index ebfed5323ba..19010daf831 100644
--- a/litellm/integrations/openmeter.py
+++ b/litellm/integrations/openmeter.py
@@ -65,9 +65,12 @@ class OpenMeterLogger(CustomLogger):
"total_tokens": response_obj["usage"].get("total_tokens"),
}
- subject = (kwargs.get("user", None),) # end-user passed in via 'user' param
- if not subject:
+ user_param = kwargs.get("user", None) # end-user passed in via 'user' param
+ if user_param is None:
raise Exception("OpenMeter: user is required")
+
+ # Ensure subject is always a string for OpenMeter API
+ subject = str(user_param)
return {
"specversion": "1.0",
diff --git a/litellm/integrations/opentelemetry.py b/litellm/integrations/opentelemetry.py
index c51447c1169..8f92ca72edb 100644
--- a/litellm/integrations/opentelemetry.py
+++ b/litellm/integrations/opentelemetry.py
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
from opentelemetry.sdk.trace.export import SpanExporter as _SpanExporter
from opentelemetry.trace import Context as _Context
from opentelemetry.trace import Span as _Span
+ from opentelemetry.trace import Tracer as _Tracer
from litellm.proxy._types import (
ManagementEndpointLoggingPayload as _ManagementEndpointLoggingPayload,
@@ -26,12 +27,14 @@ if TYPE_CHECKING:
from litellm.proxy.proxy_server import UserAPIKeyAuth as _UserAPIKeyAuth
Span = Union[_Span, Any]
+ Tracer = Union[_Tracer, Any]
Context = Union[_Context, Any]
SpanExporter = Union[_SpanExporter, Any]
UserAPIKeyAuth = Union[_UserAPIKeyAuth, Any]
ManagementEndpointLoggingPayload = Union[_ManagementEndpointLoggingPayload, Any]
else:
Span = Any
+ Tracer = Any
SpanExporter = Any
UserAPIKeyAuth = Any
ManagementEndpointLoggingPayload = Any
@@ -313,6 +316,71 @@ class OpenTelemetry(CustomLogger):
# End Parent OTEL Sspan
parent_otel_span.end(end_time=self._to_ns(datetime.now()))
+
+ #########################################################
+ # Team/Key Based Logging Control Flow
+ #########################################################
+ def get_tracer_to_use_for_request(self, kwargs: dict) -> Tracer:
+ """
+ Get the tracer to use for this request
+
+ If dynamic headers are present, a temporary tracer is created with the dynamic headers.
+ Otherwise, the default tracer is used.
+
+ Returns:
+ Tracer: The tracer to use for this request
+ """
+ dynamic_headers = self._get_dynamic_otel_headers_from_kwargs(kwargs)
+
+ if dynamic_headers is not None:
+ # Create spans using a temporary tracer with dynamic headers
+ tracer_to_use = self._get_tracer_with_dynamic_headers(dynamic_headers)
+ verbose_logger.debug("Using dynamic headers for this request: %s", dynamic_headers)
+ else:
+ tracer_to_use = self.tracer
+
+ return tracer_to_use
+
+ def _get_dynamic_otel_headers_from_kwargs(self, kwargs) -> Optional[dict]:
+ """Extract dynamic headers from kwargs if available."""
+ standard_callback_dynamic_params: Optional[
+ StandardCallbackDynamicParams
+ ] = kwargs.get("standard_callback_dynamic_params")
+
+ if not standard_callback_dynamic_params:
+ return None
+
+ dynamic_headers = self.construct_dynamic_otel_headers(
+ standard_callback_dynamic_params=standard_callback_dynamic_params
+ )
+
+ return dynamic_headers if dynamic_headers else None
+
+ def _get_tracer_with_dynamic_headers(self, dynamic_headers: dict):
+ """Create a temporary tracer with dynamic headers for this request only."""
+ from opentelemetry.sdk.resources import Resource
+ from opentelemetry.sdk.trace import TracerProvider
+
+ # Create a temporary tracer provider with dynamic headers
+ temp_provider = TracerProvider(resource=Resource(attributes=LITELLM_RESOURCE))
+ temp_provider.add_span_processor(self._get_span_processor(dynamic_headers=dynamic_headers))
+
+ return temp_provider.get_tracer(LITELLM_TRACER_NAME)
+
+ def construct_dynamic_otel_headers(self, standard_callback_dynamic_params: StandardCallbackDynamicParams) -> Optional[dict]:
+ """
+ Construct dynamic headers from standard callback dynamic params
+
+ Note: You just need to override this method in Arize, Langfuse Otel if you want to allow team/key based logging.
+
+ Returns:
+ dict: A dictionary of dynamic headers
+ """
+ return None
+
+ #########################################################
+ # End of Team/Key Based Logging Control Flow
+ #########################################################
def _handle_sucess(self, kwargs, response_obj, start_time, end_time):
from opentelemetry import trace
@@ -323,12 +391,11 @@ class OpenTelemetry(CustomLogger):
kwargs,
self.config,
)
+
_parent_context, parent_otel_span = self._get_span_context(kwargs)
-
- self._add_dynamic_span_processor_if_needed(kwargs)
-
- # Span 1: Requst sent to litellm SDK
- span = self.tracer.start_span(
+ # Span 1: Request sent to litellm SDK
+ otel_tracer: Tracer = self.get_tracer_to_use_for_request(kwargs)
+ span = otel_tracer.start_span(
name=self._get_span_name(kwargs),
start_time=self._to_ns(start_time),
context=_parent_context,
@@ -342,7 +409,7 @@ class OpenTelemetry(CustomLogger):
pass
else:
# Span 2: Raw Request / Response to LLM
- raw_request_span = self.tracer.start_span(
+ raw_request_span = otel_tracer.start_span(
name=RAW_REQUEST_SPAN_NAME,
start_time=self._to_ns(start_time),
context=trace.set_span_in_context(span),
@@ -387,7 +454,8 @@ class OpenTelemetry(CustomLogger):
if end_time_float is not None:
end_time_datetime = datetime.fromtimestamp(end_time_float)
- guardrail_span = self.tracer.start_span(
+ otel_tracer: Tracer = self.get_tracer_to_use_for_request(kwargs)
+ guardrail_span = otel_tracer.start_span(
name="guardrail",
start_time=self._to_ns(start_time_datetime),
context=context,
@@ -420,44 +488,6 @@ class OpenTelemetry(CustomLogger):
guardrail_span.end(end_time=self._to_ns(end_time_datetime))
- def _add_dynamic_span_processor_if_needed(self, kwargs):
- """
- Helper method to add a span processor with dynamic headers if needed.
-
- This allows for per-request configuration of telemetry exporters by
- extracting headers from standard_callback_dynamic_params.
- """
- from opentelemetry import trace
-
- standard_callback_dynamic_params: Optional[
- StandardCallbackDynamicParams
- ] = kwargs.get("standard_callback_dynamic_params")
- if not standard_callback_dynamic_params:
- return
-
- # Extract headers from dynamic params
- dynamic_headers = {}
-
- # Handle Arize headers
- if standard_callback_dynamic_params.get("arize_space_key"):
- dynamic_headers["space_key"] = standard_callback_dynamic_params.get(
- "arize_space_key"
- )
- if standard_callback_dynamic_params.get("arize_api_key"):
- dynamic_headers["api_key"] = standard_callback_dynamic_params.get(
- "arize_api_key"
- )
-
- # Only create a span processor if we have headers to use
- if len(dynamic_headers) > 0:
- from opentelemetry.sdk.trace import TracerProvider
-
- provider = trace.get_tracer_provider()
- if isinstance(provider, TracerProvider):
- span_processor = self._get_span_processor(
- dynamic_headers=dynamic_headers
- )
- provider.add_span_processor(span_processor)
def _handle_failure(self, kwargs, response_obj, start_time, end_time):
from opentelemetry.trace import Status, StatusCode
@@ -470,7 +500,8 @@ class OpenTelemetry(CustomLogger):
_parent_context, parent_otel_span = self._get_span_context(kwargs)
# Span 1: Requst sent to litellm SDK
- span = self.tracer.start_span(
+ otel_tracer: Tracer = self.get_tracer_to_use_for_request(kwargs)
+ span = otel_tracer.start_span(
name=self._get_span_name(kwargs),
start_time=self._to_ns(start_time),
context=_parent_context,
@@ -579,7 +610,9 @@ class OpenTelemetry(CustomLogger):
)
return
elif self.callback_name == "langfuse_otel":
- from litellm.integrations.langfuse.langfuse_otel import LangfuseOtelLogger
+ from litellm.integrations.langfuse.langfuse_otel import (
+ LangfuseOtelLogger,
+ )
LangfuseOtelLogger.set_langfuse_otel_attributes(
span, kwargs, response_obj
diff --git a/litellm/integrations/prometheus.py b/litellm/integrations/prometheus.py
index 9aea69c34a6..0583150b961 100644
--- a/litellm/integrations/prometheus.py
+++ b/litellm/integrations/prometheus.py
@@ -353,29 +353,289 @@ class PrometheusLogger(CustomLogger):
verbose_logger.debug(f"prometheus config: {config}")
- label_filters = {}
+ # Parse and validate all configuration groups
+ parsed_configs = []
self.enabled_metrics = set()
-
- # Parse each configuration group
+
for group_config in config:
# Validate configuration using Pydantic
if isinstance(group_config, dict):
parsed_config = PrometheusMetricsConfig(**group_config)
else:
parsed_config = group_config
-
- # Add enabled metrics to the set
+
+ parsed_configs.append(parsed_config)
self.enabled_metrics.update(parsed_config.metrics)
- # Set label filters for each metric in this group
- for metric_name in parsed_config.metrics:
- if parsed_config.include_labels:
- label_filters[metric_name] = parsed_config.include_labels
+ # Validate all configurations
+ validation_results = self._validate_all_configurations(parsed_configs)
+
+ if validation_results.has_errors:
+ self._pretty_print_validation_errors(validation_results)
+ error_message = "Configuration validation failed:\n" + "\n".join(validation_results.all_error_messages)
+ raise ValueError(error_message)
+ # Build label filters from valid configurations
+ label_filters = self._build_label_filters(parsed_configs)
+
# Pretty print the processed configuration
self._pretty_print_prometheus_config(label_filters)
-
return label_filters
+
+ def _validate_all_configurations(self, parsed_configs: List) -> ValidationResults:
+ """Validate all metric configurations and return collected errors"""
+ metric_errors = []
+ label_errors = []
+
+ for config in parsed_configs:
+ for metric_name in config.metrics:
+ # Validate metric name
+ metric_error = self._validate_single_metric_name(metric_name)
+ if metric_error:
+ metric_errors.append(metric_error)
+ continue # Skip label validation if metric name is invalid
+
+ # Validate labels if provided
+ if config.include_labels:
+ label_error = self._validate_single_metric_labels(metric_name, config.include_labels)
+ if label_error:
+ label_errors.append(label_error)
+
+ return ValidationResults(metric_errors=metric_errors, label_errors=label_errors)
+
+ def _validate_single_metric_name(self, metric_name: str) -> Optional[MetricValidationError]:
+ """Validate a single metric name"""
+ from typing import get_args
+ if metric_name not in set(get_args(DEFINED_PROMETHEUS_METRICS)):
+ return MetricValidationError(
+ metric_name=metric_name,
+ valid_metrics=get_args(DEFINED_PROMETHEUS_METRICS)
+ )
+ return None
+
+ def _validate_single_metric_labels(self, metric_name: str, labels: List[str]) -> Optional[LabelValidationError]:
+ """Validate labels for a single metric"""
+ from typing import cast
+
+ # Get valid labels for this metric from PrometheusMetricLabels
+ valid_labels = PrometheusMetricLabels.get_labels(cast(DEFINED_PROMETHEUS_METRICS, metric_name))
+
+ # Find invalid labels
+ invalid_labels = [label for label in labels if label not in valid_labels]
+
+ if invalid_labels:
+ return LabelValidationError(
+ metric_name=metric_name,
+ invalid_labels=invalid_labels,
+ valid_labels=valid_labels
+ )
+ return None
+
+ def _build_label_filters(self, parsed_configs: List) -> Dict[str, List[str]]:
+ """Build label filters from validated configurations"""
+ label_filters = {}
+
+ for config in parsed_configs:
+ for metric_name in config.metrics:
+ if config.include_labels:
+ # Only add if metric name is valid (validation already passed)
+ if self._validate_single_metric_name(metric_name) is None:
+ label_filters[metric_name] = config.include_labels
+
+ return label_filters
+
+ def _validate_configured_metric_labels(self, metric_name: str, labels: List[str]):
+ """
+ Ensure that all the configured labels are valid for the metric
+
+ Raises ValueError if the metric labels are invalid and pretty prints the error
+ """
+ label_error = self._validate_single_metric_labels(metric_name, labels)
+ if label_error:
+ self._pretty_print_invalid_labels_error(
+ metric_name=label_error.metric_name,
+ invalid_labels=label_error.invalid_labels,
+ valid_labels=label_error.valid_labels
+ )
+ raise ValueError(label_error.message)
+
+ return True
+
+ #########################################################
+ # Pretty print functions
+ #########################################################
+
+ def _pretty_print_validation_errors(self, validation_results: ValidationResults) -> None:
+ """Pretty print all validation errors using rich"""
+ try:
+ from rich.console import Console
+ from rich.panel import Panel
+ from rich.table import Table
+ from rich.text import Text
+
+ console = Console()
+
+ # Create error panel title
+ title = Text("🚨🚨 Configuration Validation Errors", style="bold red")
+
+ # Print main error panel
+ console.print("\n")
+ console.print(Panel(title, border_style="red"))
+
+ # Show invalid metric names if any
+ if validation_results.metric_errors:
+ invalid_metrics = [e.metric_name for e in validation_results.metric_errors]
+ valid_metrics = validation_results.metric_errors[0].valid_metrics # All should have same valid metrics
+
+ metrics_error_text = Text(
+ f"Invalid Metric Names: {', '.join(invalid_metrics)}",
+ style="bold red"
+ )
+ console.print(Panel(metrics_error_text, border_style="red"))
+
+ metrics_table = Table(
+ title="📊 Valid Metric Names",
+ show_header=True,
+ header_style="bold green",
+ title_justify="left",
+ border_style="green",
+ )
+ metrics_table.add_column("Available Metrics", style="cyan", no_wrap=True)
+
+ for metric in sorted(valid_metrics):
+ metrics_table.add_row(metric)
+
+ console.print(metrics_table)
+
+ # Show invalid labels if any
+ if validation_results.label_errors:
+ for error in validation_results.label_errors:
+ labels_error_text = Text(
+ f"Invalid Labels for '{error.metric_name}': {', '.join(error.invalid_labels)}",
+ style="bold red"
+ )
+ console.print(Panel(labels_error_text, border_style="red"))
+
+ labels_table = Table(
+ title=f"🏷️ Valid Labels for '{error.metric_name}'",
+ show_header=True,
+ header_style="bold green",
+ title_justify="left",
+ border_style="green",
+ )
+ labels_table.add_column("Valid Labels", style="cyan", no_wrap=True)
+
+ for label in sorted(error.valid_labels):
+ labels_table.add_row(label)
+
+ console.print(labels_table)
+
+ console.print("\n")
+
+ except ImportError:
+ # Fallback to simple logging if rich is not available
+ for metric_error in validation_results.metric_errors:
+ verbose_logger.error(metric_error.message)
+ for label_error in validation_results.label_errors:
+ verbose_logger.error(label_error.message)
+
+ def _pretty_print_invalid_labels_error(
+ self, metric_name: str, invalid_labels: List[str], valid_labels: List[str]
+ ) -> None:
+ """Pretty print error message for invalid labels using rich"""
+ try:
+ from rich.console import Console
+ from rich.panel import Panel
+ from rich.table import Table
+ from rich.text import Text
+
+ console = Console()
+
+ # Create error panel title
+ title = Text(
+ f"🚨🚨 Invalid Labels for Metric: '{metric_name}'\nInvalid labels: {', '.join(invalid_labels)}\nPlease specify only valid labels below",
+ style="bold red"
+ )
+
+ # Create valid labels table
+ labels_table = Table(
+ title="🏷️ Valid Labels for this Metric",
+ show_header=True,
+ header_style="bold green",
+ title_justify="left",
+ border_style="green",
+ )
+ labels_table.add_column("Valid Labels", style="cyan", no_wrap=True)
+
+ for label in sorted(valid_labels):
+ labels_table.add_row(label)
+
+ # Print everything in a nice panel
+ console.print("\n")
+ console.print(Panel(title, border_style="red"))
+ console.print(labels_table)
+ console.print("\n")
+
+ except ImportError:
+ # Fallback to simple logging if rich is not available
+ verbose_logger.error(
+ f"Invalid labels for metric '{metric_name}': {invalid_labels}. Valid labels: {sorted(valid_labels)}"
+ )
+
+ def _pretty_print_invalid_metric_error(
+ self, invalid_metric_name: str, valid_metrics: tuple
+ ) -> None:
+ """Pretty print error message for invalid metric name using rich"""
+ try:
+ from rich.console import Console
+ from rich.panel import Panel
+ from rich.table import Table
+ from rich.text import Text
+
+ console = Console()
+
+ # Create error panel title
+ title = Text(f"🚨🚨 Invalid Metric Name: '{invalid_metric_name}'\nPlease specify one of the allowed metrics below", style="bold red")
+
+ # Create valid metrics table
+ metrics_table = Table(
+ title="📊 Valid Metric Names",
+ show_header=True,
+ header_style="bold green",
+ title_justify="left",
+ border_style="green",
+ )
+ metrics_table.add_column("Available Metrics", style="cyan", no_wrap=True)
+
+ for metric in sorted(valid_metrics):
+ metrics_table.add_row(metric)
+
+ # Print everything in a nice panel
+ console.print("\n")
+ console.print(Panel(title, border_style="red"))
+ console.print(metrics_table)
+ console.print("\n")
+
+ except ImportError:
+ # Fallback to simple logging if rich is not available
+ verbose_logger.error(
+ f"Invalid metric name: {invalid_metric_name}. Valid metrics: {sorted(valid_metrics)}"
+ )
+
+ #########################################################
+ # End of pretty print functions
+ #########################################################
+
+ def _valid_metric_name(self, metric_name: str):
+ """
+ Raises ValueError if the metric name is invalid and pretty prints the error
+ """
+ error = self._validate_single_metric_name(metric_name)
+ if error:
+ self._pretty_print_invalid_metric_error(
+ invalid_metric_name=error.metric_name,
+ valid_metrics=error.valid_metrics)
+ raise ValueError(error.message)
def _pretty_print_prometheus_config(
self, label_filters: Dict[str, List[str]]
@@ -447,6 +707,7 @@ class PrometheusLogger(CustomLogger):
)
verbose_logger.info(f"Label filters: {label_filters}")
+
def _is_metric_enabled(self, metric_name: str) -> bool:
"""Check if a metric is enabled based on configuration"""
# If no specific configuration is provided, enable all metrics (default behavior)
diff --git a/litellm/integrations/prompt_management_base.py b/litellm/integrations/prompt_management_base.py
index c9e7adbccbd..4a8bcd2e249 100644
--- a/litellm/integrations/prompt_management_base.py
+++ b/litellm/integrations/prompt_management_base.py
@@ -34,6 +34,7 @@ class PromptManagementBase(ABC):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> PromptManagementClient:
pass
@@ -51,12 +52,14 @@ class PromptManagementBase(ABC):
client_messages: List[AllMessageValues],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> PromptManagementClient:
compiled_prompt_client = self._compile_prompt_helper(
prompt_id=prompt_id,
prompt_variables=prompt_variables,
dynamic_callback_params=dynamic_callback_params,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
try:
@@ -86,6 +89,7 @@ class PromptManagementBase(ABC):
prompt_variables: Optional[dict],
dynamic_callback_params: StandardCallbackDynamicParams,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
if prompt_id is None:
raise ValueError("prompt_id is required for Prompt Management Base class")
@@ -100,6 +104,7 @@ class PromptManagementBase(ABC):
client_messages=messages,
dynamic_callback_params=dynamic_callback_params,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
completed_messages = prompt_template["completed_messages"] or messages
diff --git a/litellm/integrations/s3_v2.py b/litellm/integrations/s3_v2.py
index 121a491cfcf..09a58498e15 100644
--- a/litellm/integrations/s3_v2.py
+++ b/litellm/integrations/s3_v2.py
@@ -2,7 +2,7 @@
s3 Bucket Logging Integration
async_log_success_event: Processes the event, stores it in memory for DEFAULT_S3_FLUSH_INTERVAL_SECONDS seconds or until DEFAULT_S3_BATCH_SIZE and then flushes to s3
-
+async_log_failure_event: Processes the event, stores it in memory for DEFAULT_S3_FLUSH_INTERVAL_SECONDS seconds or until DEFAULT_S3_BATCH_SIZE and then flushes to s3
NOTE 1: S3 does not provide a BATCH PUT API endpoint, so we create tasks to upload each element individually
"""
@@ -197,6 +197,24 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM):
return
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
+ await self._async_log_event_base(
+ kwargs=kwargs,
+ response_obj=response_obj,
+ start_time=start_time,
+ end_time=end_time,
+ )
+
+ async def async_log_failure_event(self, kwargs, response_obj, start_time, end_time):
+ await self._async_log_event_base(
+ kwargs=kwargs,
+ response_obj=response_obj,
+ start_time=start_time,
+ end_time=end_time,
+ )
+ pass
+
+
+ async def _async_log_event_base(self, kwargs, response_obj, start_time, end_time):
try:
verbose_logger.debug(
f"s3 Logging - Enters logging function for model {kwargs}"
@@ -224,6 +242,7 @@ class S3Logger(CustomBatchLogger, BaseAWSLLM):
verbose_logger.exception(f"s3 Layer Error - {str(e)}")
pass
+
async def async_upload_data_to_s3(
self, batch_logging_element: s3BatchLoggingElement
):
diff --git a/litellm/integrations/sqs.py b/litellm/integrations/sqs.py
new file mode 100644
index 00000000000..2a0c73dfdbf
--- /dev/null
+++ b/litellm/integrations/sqs.py
@@ -0,0 +1,275 @@
+"""SQS Logging Integration
+
+This logger sends ``StandardLoggingPayload`` entries to an AWS SQS queue.
+
+"""
+
+from __future__ import annotations
+
+import asyncio
+from typing import List, Optional
+
+import litellm
+from litellm._logging import print_verbose, verbose_logger
+from litellm.constants import (
+ DEFAULT_SQS_BATCH_SIZE,
+ DEFAULT_SQS_FLUSH_INTERVAL_SECONDS,
+ SQS_API_VERSION,
+ SQS_SEND_MESSAGE_ACTION,
+)
+from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
+from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
+from litellm.llms.custom_httpx.http_handler import (
+ get_async_httpx_client,
+ httpxSpecialProvider,
+)
+from litellm.types.utils import StandardLoggingPayload
+
+from .custom_batch_logger import CustomBatchLogger
+
+
+class SQSLogger(CustomBatchLogger, BaseAWSLLM):
+ """Batching logger that writes logs to an AWS SQS queue."""
+
+ def __init__(
+ self,
+ sqs_queue_url: Optional[str] = None,
+ sqs_region_name: Optional[str] = None,
+ sqs_api_version: Optional[str] = None,
+ sqs_use_ssl: bool = True,
+ sqs_verify: Optional[bool] = None,
+ sqs_endpoint_url: Optional[str] = None,
+ sqs_aws_access_key_id: Optional[str] = None,
+ sqs_aws_secret_access_key: Optional[str] = None,
+ sqs_aws_session_token: Optional[str] = None,
+ sqs_aws_session_name: Optional[str] = None,
+ sqs_aws_profile_name: Optional[str] = None,
+ sqs_aws_role_name: Optional[str] = None,
+ sqs_aws_web_identity_token: Optional[str] = None,
+ sqs_aws_sts_endpoint: Optional[str] = None,
+ sqs_flush_interval: Optional[int] = DEFAULT_SQS_FLUSH_INTERVAL_SECONDS,
+ sqs_batch_size: Optional[int] = DEFAULT_SQS_BATCH_SIZE,
+ sqs_config=None,
+ **kwargs,
+ ) -> None:
+ try:
+ verbose_logger.debug(
+ f"in init sqs logger - sqs_callback_params {litellm.aws_sqs_callback_params}"
+ )
+
+ self.async_httpx_client = get_async_httpx_client(
+ llm_provider=httpxSpecialProvider.LoggingCallback,
+ )
+
+ self._init_sqs_params(
+ sqs_queue_url=sqs_queue_url,
+ sqs_region_name=sqs_region_name,
+ sqs_api_version=sqs_api_version,
+ sqs_use_ssl=sqs_use_ssl,
+ sqs_verify=sqs_verify,
+ sqs_endpoint_url=sqs_endpoint_url,
+ sqs_aws_access_key_id=sqs_aws_access_key_id,
+ sqs_aws_secret_access_key=sqs_aws_secret_access_key,
+ sqs_aws_session_token=sqs_aws_session_token,
+ sqs_aws_session_name=sqs_aws_session_name,
+ sqs_aws_profile_name=sqs_aws_profile_name,
+ sqs_aws_role_name=sqs_aws_role_name,
+ sqs_aws_web_identity_token=sqs_aws_web_identity_token,
+ sqs_aws_sts_endpoint=sqs_aws_sts_endpoint,
+ sqs_config=sqs_config,
+ )
+
+ asyncio.create_task(self.periodic_flush())
+ self.flush_lock = asyncio.Lock()
+
+ verbose_logger.debug(
+ f"sqs flush interval: {sqs_flush_interval}, sqs batch size: {sqs_batch_size}"
+ )
+
+ CustomBatchLogger.__init__(
+ self,
+ flush_lock=self.flush_lock,
+ flush_interval=sqs_flush_interval,
+ batch_size=sqs_batch_size,
+ )
+
+ self.log_queue: List[StandardLoggingPayload] = []
+
+ BaseAWSLLM.__init__(self)
+
+ except Exception as e:
+ print_verbose(f"Got exception on init sqs client {str(e)}")
+ raise e
+
+ def _init_sqs_params(
+ self,
+ sqs_queue_url: Optional[str] = None,
+ sqs_region_name: Optional[str] = None,
+ sqs_api_version: Optional[str] = None,
+ sqs_use_ssl: bool = True,
+ sqs_verify: Optional[bool] = None,
+ sqs_endpoint_url: Optional[str] = None,
+ sqs_aws_access_key_id: Optional[str] = None,
+ sqs_aws_secret_access_key: Optional[str] = None,
+ sqs_aws_session_token: Optional[str] = None,
+ sqs_aws_session_name: Optional[str] = None,
+ sqs_aws_profile_name: Optional[str] = None,
+ sqs_aws_role_name: Optional[str] = None,
+ sqs_aws_web_identity_token: Optional[str] = None,
+ sqs_aws_sts_endpoint: Optional[str] = None,
+ sqs_config=None,
+ ) -> None:
+ litellm.aws_sqs_callback_params = litellm.aws_sqs_callback_params or {}
+
+ # read in .env variables - example os.environ/AWS_BUCKET_NAME
+ for key, value in litellm.aws_sqs_callback_params.items():
+ if isinstance(value, str) and value.startswith("os.environ/"):
+ litellm.aws_sqs_callback_params[key] = litellm.get_secret(value)
+
+ self.sqs_queue_url = (
+ litellm.aws_sqs_callback_params.get("sqs_queue_url") or sqs_queue_url
+ )
+ self.sqs_region_name = (
+ litellm.aws_sqs_callback_params.get("sqs_region_name") or sqs_region_name
+ )
+ self.sqs_api_version = (
+ litellm.aws_sqs_callback_params.get("sqs_api_version") or sqs_api_version
+ )
+ self.sqs_use_ssl = (
+ litellm.aws_sqs_callback_params.get("sqs_use_ssl", True) or sqs_use_ssl
+ )
+ self.sqs_verify = litellm.aws_sqs_callback_params.get("sqs_verify") or sqs_verify
+ self.sqs_endpoint_url = (
+ litellm.aws_sqs_callback_params.get("sqs_endpoint_url") or sqs_endpoint_url
+ )
+ self.sqs_aws_access_key_id = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_access_key_id")
+ or sqs_aws_access_key_id
+ )
+
+ self.sqs_aws_secret_access_key = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_secret_access_key")
+ or sqs_aws_secret_access_key
+ )
+
+ self.sqs_aws_session_token = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_session_token")
+ or sqs_aws_session_token
+ )
+
+ self.sqs_aws_session_name = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_session_name") or sqs_aws_session_name
+ )
+
+ self.sqs_aws_profile_name = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_profile_name") or sqs_aws_profile_name
+ )
+
+ self.sqs_aws_role_name = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_role_name") or sqs_aws_role_name
+ )
+
+ self.sqs_aws_web_identity_token = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_web_identity_token")
+ or sqs_aws_web_identity_token
+ )
+
+ self.sqs_aws_sts_endpoint = (
+ litellm.aws_sqs_callback_params.get("sqs_aws_sts_endpoint") or sqs_aws_sts_endpoint
+ )
+
+ self.sqs_config = litellm.aws_sqs_callback_params.get("sqs_config") or sqs_config
+
+ async def async_log_success_event(
+ self, kwargs, response_obj, start_time, end_time
+ ) -> None:
+ try:
+ verbose_logger.debug(
+ "SQS Logging - Enters logging function for model %s", kwargs
+ )
+ standard_logging_payload = kwargs.get("standard_logging_object")
+ if standard_logging_payload is None:
+ raise ValueError("standard_logging_payload is None")
+
+ self.log_queue.append(standard_logging_payload)
+ verbose_logger.debug(
+ "sqs logging: queue length %s, batch size %s",
+ len(self.log_queue),
+ self.batch_size,
+ )
+ except Exception as e:
+ verbose_logger.exception(f"sqs Layer Error - {str(e)}")
+
+ async def async_send_batch(self) -> None:
+ verbose_logger.debug(
+ f"sqs logger - sending batch of {len(self.log_queue)}"
+ )
+ if not self.log_queue:
+ return
+
+ for payload in self.log_queue:
+ asyncio.create_task(self.async_send_message(payload))
+
+ async def async_send_message(self, payload: StandardLoggingPayload) -> None:
+ try:
+ from urllib.parse import quote
+
+ import requests
+ from botocore.auth import SigV4Auth
+ from botocore.awsrequest import AWSRequest
+
+ from litellm.litellm_core_utils.asyncify import asyncify
+
+ asyncified_get_credentials = asyncify(self.get_credentials)
+ credentials = await asyncified_get_credentials(
+ aws_access_key_id=self.sqs_aws_access_key_id,
+ aws_secret_access_key=self.sqs_aws_secret_access_key,
+ aws_session_token=self.sqs_aws_session_token,
+ aws_region_name=self.sqs_region_name,
+ aws_session_name=self.sqs_aws_session_name,
+ aws_profile_name=self.sqs_aws_profile_name,
+ aws_role_name=self.sqs_aws_role_name,
+ aws_web_identity_token=self.sqs_aws_web_identity_token,
+ aws_sts_endpoint=self.sqs_aws_sts_endpoint,
+ )
+
+ if self.sqs_queue_url is None:
+ raise ValueError("sqs_queue_url not set")
+
+ json_string = safe_dumps(payload)
+
+ body = (
+ f"Action={SQS_SEND_MESSAGE_ACTION}&Version={SQS_API_VERSION}&MessageBody="
+ + quote(json_string, safe="")
+ )
+
+ headers = {
+ "Content-Type": "application/x-www-form-urlencoded",
+ }
+
+ req = requests.Request(
+ "POST", self.sqs_queue_url, data=body, headers=headers
+ )
+ prepped = req.prepare()
+
+ aws_request = AWSRequest(
+ method=prepped.method,
+ url=prepped.url,
+ data=prepped.body,
+ headers=prepped.headers,
+ )
+ SigV4Auth(credentials, "sqs", self.sqs_region_name).add_auth(
+ aws_request
+ )
+
+ signed_headers = dict(aws_request.headers.items())
+
+ response = await self.async_httpx_client.post(
+ self.sqs_queue_url,
+ data=body,
+ headers=signed_headers,
+ )
+ response.raise_for_status()
+ except Exception as e:
+ verbose_logger.exception(f"Error sending to SQS: {str(e)}")
+
diff --git a/litellm/integrations/vector_store_integrations/bedrock_vector_store.py b/litellm/integrations/vector_store_integrations/bedrock_vector_store.py
index a00acefb6a3..d3ba3a8ebd6 100644
--- a/litellm/integrations/vector_store_integrations/bedrock_vector_store.py
+++ b/litellm/integrations/vector_store_integrations/bedrock_vector_store.py
@@ -77,6 +77,7 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
litellm_logging_obj: LiteLLMLoggingObj,
tools: Optional[List[Dict]] = None,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Retrieves the context from the Bedrock Knowledge Base and appends it to the messages.
@@ -129,9 +130,9 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
)
)
- litellm_logging_obj.model_call_details[
- "vector_store_request_metadata"
- ] = vector_store_request_metadata
+ litellm_logging_obj.model_call_details["vector_store_request_metadata"] = (
+ vector_store_request_metadata
+ )
return model, messages, non_default_params
@@ -143,9 +144,9 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
"""
Transform a BedrockKBResponse to a VectorStoreSearchResponse
"""
- retrieval_results: Optional[
- List[BedrockKBRetrievalResult]
- ] = bedrock_kb_response.get("retrievalResults", None)
+ retrieval_results: Optional[List[BedrockKBRetrievalResult]] = (
+ bedrock_kb_response.get("retrievalResults", None)
+ )
vector_store_search_response: VectorStoreSearchResponse = (
VectorStoreSearchResponse(search_query=query, data=[])
)
diff --git a/litellm/integrations/vector_stores/bedrock_vector_store.py b/litellm/integrations/vector_stores/bedrock_vector_store.py
index a00acefb6a3..d3ba3a8ebd6 100644
--- a/litellm/integrations/vector_stores/bedrock_vector_store.py
+++ b/litellm/integrations/vector_stores/bedrock_vector_store.py
@@ -77,6 +77,7 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
litellm_logging_obj: LiteLLMLoggingObj,
tools: Optional[List[Dict]] = None,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
"""
Retrieves the context from the Bedrock Knowledge Base and appends it to the messages.
@@ -129,9 +130,9 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
)
)
- litellm_logging_obj.model_call_details[
- "vector_store_request_metadata"
- ] = vector_store_request_metadata
+ litellm_logging_obj.model_call_details["vector_store_request_metadata"] = (
+ vector_store_request_metadata
+ )
return model, messages, non_default_params
@@ -143,9 +144,9 @@ class BedrockVectorStore(BaseVectorStore, BaseAWSLLM):
"""
Transform a BedrockKBResponse to a VectorStoreSearchResponse
"""
- retrieval_results: Optional[
- List[BedrockKBRetrievalResult]
- ] = bedrock_kb_response.get("retrievalResults", None)
+ retrieval_results: Optional[List[BedrockKBRetrievalResult]] = (
+ bedrock_kb_response.get("retrievalResults", None)
+ )
vector_store_search_response: VectorStoreSearchResponse = (
VectorStoreSearchResponse(search_query=query, data=[])
)
diff --git a/litellm/litellm_core_utils/custom_logger_registry.py b/litellm/litellm_core_utils/custom_logger_registry.py
index 1b75cc3e3df..e6180fa8d9e 100644
--- a/litellm/litellm_core_utils/custom_logger_registry.py
+++ b/litellm/litellm_core_utils/custom_logger_registry.py
@@ -32,6 +32,7 @@ from litellm.integrations.opentelemetry import OpenTelemetry
from litellm.integrations.opik.opik import OpikLogger
from litellm.integrations.prometheus import PrometheusLogger
from litellm.integrations.s3_v2 import S3Logger
+from litellm.integrations.sqs import SQSLogger
from litellm.integrations.vector_store_integrations.bedrock_vector_store import (
BedrockVectorStore,
)
@@ -73,6 +74,7 @@ class CustomLoggerRegistry:
"bedrock_vector_store": BedrockVectorStore,
"deepeval": DeepEvalLogger,
"s3_v2": S3Logger,
+ "aws_sqs": SQSLogger,
"dynamic_rate_limiter": _PROXY_DynamicRateLimitHandler,
}
diff --git a/litellm/litellm_core_utils/exception_mapping_utils.py b/litellm/litellm_core_utils/exception_mapping_utils.py
index ad5060c533b..7905cd571b9 100644
--- a/litellm/litellm_core_utils/exception_mapping_utils.py
+++ b/litellm/litellm_core_utils/exception_mapping_utils.py
@@ -28,23 +28,31 @@ class ExceptionCheckers:
"""
Helper class for checking various error conditions in exception strings.
"""
-
+
@staticmethod
def is_error_str_rate_limit(error_str: str) -> bool:
"""
Check if an error string indicates a rate limit error.
-
+
Args:
error_str: The error string to check
-
+
Returns:
True if the error indicates a rate limit, False otherwise
"""
if not isinstance(error_str, str):
return False
-
- return "429" in error_str or "rate limit" in error_str.lower()
-
+
+ if "429" in error_str or "rate limit" in error_str.lower():
+ return True
+
+ #######################################
+ # Mistral API returns this error string
+ #########################################
+ if "service tier capacity exceeded" in error_str.lower():
+ return True
+
+ return False
@staticmethod
def is_error_str_context_window_exceeded(error_str: str) -> bool:
@@ -289,6 +297,7 @@ def exception_type( # type: ignore # noqa: PLR0915
or custom_llm_provider == "text-completion-openai"
or custom_llm_provider == "custom_openai"
or custom_llm_provider in litellm.openai_compatible_providers
+ or custom_llm_provider == "mistral"
):
# custom_llm_provider is openai, make it OpenAI
message = get_error_message(error_obj=original_exception)
diff --git a/litellm/litellm_core_utils/get_llm_provider_logic.py b/litellm/litellm_core_utils/get_llm_provider_logic.py
index 70e61094497..b1f6b767516 100644
--- a/litellm/litellm_core_utils/get_llm_provider_logic.py
+++ b/litellm/litellm_core_utils/get_llm_provider_logic.py
@@ -624,6 +624,14 @@ def _get_openai_compatible_provider_info( # noqa: PLR0915
or "https://api.galadriel.com/v1"
) # type: ignore
dynamic_api_key = api_key or get_secret_str("GALADRIEL_API_KEY")
+ elif custom_llm_provider == "github_copilot":
+ (
+ api_base,
+ dynamic_api_key,
+ custom_llm_provider,
+ ) = litellm.GithubCopilotConfig()._get_openai_compatible_provider_info(
+ model, api_base, api_key, custom_llm_provider
+ )
elif custom_llm_provider == "novita":
api_base = (
api_base
diff --git a/litellm/litellm_core_utils/initialize_dynamic_callback_params.py b/litellm/litellm_core_utils/initialize_dynamic_callback_params.py
index e5a19e7bddc..c425319b4d4 100644
--- a/litellm/litellm_core_utils/initialize_dynamic_callback_params.py
+++ b/litellm/litellm_core_utils/initialize_dynamic_callback_params.py
@@ -18,6 +18,7 @@ def initialize_standard_callback_dynamic_params(
_supported_callback_params = (
StandardCallbackDynamicParams.__annotations__.keys()
)
+
for param in _supported_callback_params:
if param in kwargs:
_param_value = kwargs.pop(param)
diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py
index f7aa59db973..42d91ab6d42 100644
--- a/litellm/litellm_core_utils/litellm_logging.py
+++ b/litellm/litellm_core_utils/litellm_logging.py
@@ -44,6 +44,8 @@ from litellm.caching.caching_handler import LLMCachingHandler
from litellm.constants import (
DEFAULT_MOCK_RESPONSE_COMPLETION_TOKEN_COUNT,
DEFAULT_MOCK_RESPONSE_PROMPT_TOKEN_COUNT,
+ SENTRY_DENYLIST,
+ SENTRY_PII_DENYLIST,
)
from litellm.cost_calculator import (
RealtimeAPITokenUsageProcessor,
@@ -56,6 +58,7 @@ from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.integrations.custom_logger import CustomLogger
from litellm.integrations.deepeval.deepeval import DeepEvalLogger
from litellm.integrations.mlflow import MlflowLogger
+from litellm.integrations.sqs import SQSLogger
from litellm.integrations.vector_store_integrations.bedrock_vector_store import (
BedrockVectorStore,
)
@@ -432,6 +435,7 @@ class Logging(LiteLLMLoggingBaseClass):
checks if langfuse_secret_key, gcs_bucket_name in kwargs and sets the corresponding attributes in StandardCallbackDynamicParams
"""
+
return _initialize_standard_callback_dynamic_params(kwargs)
def initialize_standard_built_in_tools_params(
@@ -553,6 +557,7 @@ class Logging(LiteLLMLoggingBaseClass):
prompt_variables: Optional[dict],
prompt_management_logger: Optional[CustomLogger] = None,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
custom_logger = (
prompt_management_logger
@@ -574,6 +579,7 @@ class Logging(LiteLLMLoggingBaseClass):
prompt_variables=prompt_variables,
dynamic_callback_params=self.standard_callback_dynamic_params,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
self.messages = messages
return model, messages, non_default_params
@@ -588,6 +594,7 @@ class Logging(LiteLLMLoggingBaseClass):
prompt_management_logger: Optional[CustomLogger] = None,
tools: Optional[List[Dict]] = None,
prompt_label: Optional[str] = None,
+ prompt_version: Optional[int] = None,
) -> Tuple[str, List[AllMessageValues], dict]:
custom_logger = (
prompt_management_logger
@@ -611,6 +618,7 @@ class Logging(LiteLLMLoggingBaseClass):
litellm_logging_obj=self,
tools=tools,
prompt_label=prompt_label,
+ prompt_version=prompt_version,
)
self.messages = messages
return model, messages, non_default_params
@@ -2903,31 +2911,37 @@ def _get_masked_values(
]
return {
k: (
- (
- v[: unmasked_length // 2]
- + "*" * number_of_asterisks
- + v[-unmasked_length // 2 :]
- )
- if (
- isinstance(v, str)
- and len(v) > unmasked_length
- and number_of_asterisks is not None
+ # If ignore_sensitive_values is True, or if this key doesn't contain sensitive keywords, return original value
+ v
+ if ignore_sensitive_values
+ or not any(
+ sensitive_keyword in k.lower()
+ for sensitive_keyword in sensitive_keywords
)
else (
+ # Apply masking to sensitive keys
(
v[: unmasked_length // 2]
- + "*" * (len(v) - unmasked_length)
+ + "*" * number_of_asterisks
+ v[-unmasked_length // 2 :]
)
- if (isinstance(v, str) and len(v) > unmasked_length)
- else "*****"
+ if (
+ isinstance(v, str)
+ and len(v) > unmasked_length
+ and number_of_asterisks is not None
+ )
+ else (
+ (
+ v[: unmasked_length // 2]
+ + "*" * (len(v) - unmasked_length)
+ + v[-unmasked_length // 2 :]
+ )
+ if (isinstance(v, str) and len(v) > unmasked_length)
+ else ("*****" if isinstance(v, str) else v)
+ )
)
)
for k, v in sensitive_object.items()
- if not ignore_sensitive_values
- or not any(
- sensitive_keyword in k.lower() for sensitive_keyword in sensitive_keywords
- )
}
@@ -2948,6 +2962,8 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
[sys.executable, "-m", "pip", "install", "sentry_sdk"]
)
import sentry_sdk
+ from sentry_sdk.scrubber import EventScrubber
+
sentry_sdk_instance = sentry_sdk
sentry_trace_rate = (
os.environ.get("SENTRY_API_TRACE_RATE")
@@ -2965,6 +2981,10 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
sample_rate=float(
sentry_sample_rate if sentry_sample_rate else 1.0
),
+ send_default_pii=False, # Prevent sending Personal Identifiable Information
+ event_scrubber=EventScrubber(
+ denylist=SENTRY_DENYLIST, pii_denylist=SENTRY_PII_DENYLIST
+ ),
)
capture_exception = sentry_sdk_instance.capture_exception
add_breadcrumb = sentry_sdk_instance.add_breadcrumb
@@ -3021,6 +3041,7 @@ def set_callbacks(callback_list, function_id=None): # noqa: PLR0915
s3Logger = S3Logger()
elif callback == "wandb":
from litellm.integrations.weights_biases import WeightsBiasesLogger
+
weightsBiasesLogger = WeightsBiasesLogger()
elif callback == "logfire":
logfireLogger = LogfireLogger()
@@ -3075,6 +3096,7 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
return _openmeter_logger # type: ignore
elif logging_integration == "braintrust":
from litellm.integrations.braintrust_logging import BraintrustLogger
+
for callback in _in_memory_loggers:
if isinstance(callback, BraintrustLogger):
return callback # type: ignore
@@ -3142,6 +3164,14 @@ def _init_custom_logger_compatible_class( # noqa: PLR0915
_s3_v2_logger = S3V2Logger()
_in_memory_loggers.append(_s3_v2_logger)
return _s3_v2_logger # type: ignore
+ elif logging_integration == "aws_sqs":
+ for callback in _in_memory_loggers:
+ if isinstance(callback, SQSLogger):
+ return callback # type: ignore
+
+ _aws_sqs_logger = SQSLogger()
+ _in_memory_loggers.append(_aws_sqs_logger)
+ return _aws_sqs_logger # type: ignore
elif logging_integration == "azure_storage":
for callback in _in_memory_loggers:
if isinstance(callback, AzureBlobStorageLogger):
@@ -3433,6 +3463,7 @@ def get_custom_logger_compatible_class( # noqa: PLR0915
return callback
elif logging_integration == "braintrust":
from litellm.integrations.braintrust_logging import BraintrustLogger
+
for callback in _in_memory_loggers:
if isinstance(callback, BraintrustLogger):
return callback
@@ -3476,6 +3507,13 @@ def get_custom_logger_compatible_class( # noqa: PLR0915
for callback in _in_memory_loggers:
if isinstance(callback, S3V2Logger):
return callback
+ elif logging_integration == "aws_sqs":
+ for callback in _in_memory_loggers:
+ if isinstance(callback, SQSLogger):
+ return callback
+ _aws_sqs_logger = SQSLogger()
+ _in_memory_loggers.append(_aws_sqs_logger)
+ return _aws_sqs_logger # type: ignore
elif logging_integration == "azure_storage":
for callback in _in_memory_loggers:
if isinstance(callback, AzureBlobStorageLogger):
diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py
index f840b598106..737e3f7f982 100644
--- a/litellm/litellm_core_utils/llm_cost_calc/utils.py
+++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py
@@ -114,8 +114,8 @@ def _get_token_base_cost(model_info: ModelInfo, usage: Usage) -> Tuple[float, fl
If input_tokens > threshold and `input_cost_per_token_above_[x]k_tokens` or `input_cost_per_token_above_[x]_tokens` is set,
then we use the corresponding threshold cost.
"""
- prompt_base_cost = model_info["input_cost_per_token"]
- completion_base_cost = model_info["output_cost_per_token"]
+ prompt_base_cost = cast(float, _get_cost_per_unit(model_info, "input_cost_per_token"))
+ completion_base_cost = cast(float, _get_cost_per_unit(model_info, "output_cost_per_token"))
## CHECK IF ABOVE THRESHOLD
threshold: Optional[float] = None
@@ -128,17 +128,13 @@ def _get_token_base_cost(model_info: ModelInfo, usage: Usage) -> Tuple[float, fl
1000 if "k" in threshold_str else 1
)
if usage.prompt_tokens > threshold:
- prompt_base_cost = cast(
- float,
- model_info.get(key, prompt_base_cost),
- )
- completion_base_cost = cast(
- float,
- model_info.get(
- f"output_cost_per_token_above_{threshold_str}_tokens",
- completion_base_cost,
- ),
- )
+
+ prompt_base_cost = cast(float, _get_cost_per_unit(model_info, key, prompt_base_cost))
+ completion_base_cost = cast(float, _get_cost_per_unit(
+ model_info,
+ f"output_cost_per_token_above_{threshold_str}_tokens",
+ completion_base_cost,
+ ))
break
except (IndexError, ValueError):
continue
@@ -162,7 +158,7 @@ def calculate_cost_component(
Returns:
float: The calculated cost
"""
- cost_per_unit = model_info.get(cost_key)
+ cost_per_unit = _get_cost_per_unit(model_info, cost_key)
if (
cost_per_unit is not None
and isinstance(cost_per_unit, float)
@@ -173,6 +169,24 @@ def calculate_cost_component(
return 0.0
+def _get_cost_per_unit(model_info: ModelInfo, cost_key: str, default_value: Optional[float] = 0.0) -> Optional[float]:
+ # Sometimes the cost per unit is a string (e.g.: If a value like "3e-7" was read from the config.yaml)
+ cost_per_unit = model_info.get(cost_key)
+ if isinstance(cost_per_unit, float):
+ return cost_per_unit
+ if isinstance(cost_per_unit, int):
+ return float(cost_per_unit)
+ if isinstance(cost_per_unit, str):
+ try:
+ return float(cost_per_unit)
+ except ValueError:
+ verbose_logger.exception(
+ f"litellm.litellm_core_utils.llm_cost_calc.utils.py::calculate_cost_per_component(): Exception occured - {cost_per_unit}\nDefaulting to 0.0"
+ )
+ return default_value
+
+
+
def generic_cost_per_token(
model: str, usage: Usage, custom_llm_provider: str
) -> Tuple[float, float]:
@@ -316,13 +330,8 @@ def generic_cost_per_token(
## 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"
- )
-
- _output_cost_per_reasoning_token: Optional[float] = model_info.get(
- "output_cost_per_reasoning_token"
- )
+ _output_cost_per_audio_token = _get_cost_per_unit(model_info, "output_cost_per_audio_token", None)
+ _output_cost_per_reasoning_token = _get_cost_per_unit(model_info, "output_cost_per_reasoning_token", None)
## AUDIO COST
if not is_text_tokens_total and audio_tokens is not None and audio_tokens > 0:
diff --git a/litellm/litellm_core_utils/prompt_templates/factory.py b/litellm/litellm_core_utils/prompt_templates/factory.py
index c99c0ae726e..2a1fc6cdc88 100644
--- a/litellm/litellm_core_utils/prompt_templates/factory.py
+++ b/litellm/litellm_core_utils/prompt_templates/factory.py
@@ -2631,7 +2631,7 @@ def _convert_to_bedrock_tool_call_invoke(
id = tool["id"]
name = tool["function"].get("name", "")
arguments = tool["function"].get("arguments", "")
- arguments_dict = json.loads(arguments)
+ arguments_dict = json.loads(arguments) if arguments else {}
bedrock_tool = BedrockToolUseBlock(
input=arguments_dict, name=name, toolUseId=id
)
diff --git a/litellm/litellm_core_utils/redact_messages.py b/litellm/litellm_core_utils/redact_messages.py
index 79aeeff144c..2ef7af5eaaf 100644
--- a/litellm/litellm_core_utils/redact_messages.py
+++ b/litellm/litellm_core_utils/redact_messages.py
@@ -53,29 +53,45 @@ def perform_redaction(model_call_details: dict, result):
and "complete_streaming_response" in model_call_details
):
_streaming_response = model_call_details["complete_streaming_response"]
- for choice in _streaming_response.choices:
- if isinstance(choice, litellm.Choices):
- choice.message.content = "redacted-by-litellm"
- elif isinstance(choice, litellm.utils.StreamingChoices):
- choice.delta.content = "redacted-by-litellm"
-
- # Redact result
- if result is not None and isinstance(result, litellm.ModelResponse):
- _result = copy.deepcopy(result)
- if hasattr(_result, "choices") and _result.choices is not None:
- for choice in _result.choices:
+ if hasattr(_streaming_response, "choices"):
+ for choice in _streaming_response.choices:
if isinstance(choice, litellm.Choices):
choice.message.content = "redacted-by-litellm"
elif isinstance(choice, litellm.utils.StreamingChoices):
choice.delta.content = "redacted-by-litellm"
- return _result
- if result is not None and isinstance(result, litellm.EmbeddingResponse):
+ elif hasattr(_streaming_response, "output"):
+ # Handle ResponsesAPIResponse format
+ for output_item in _streaming_response.output:
+ if hasattr(output_item, "content") and isinstance(
+ output_item.content, list
+ ):
+ for content_part in output_item.content:
+ if hasattr(content_part, "text"):
+ content_part.text = "redacted-by-litellm"
+
+ # Redact result
+ if result is not None:
_result = copy.deepcopy(result)
- if hasattr(_result, "data") and _result.data is not None:
- _result.data = []
+ if isinstance(_result, litellm.ModelResponse):
+ if hasattr(_result, "choices") and _result.choices is not None:
+ for choice in _result.choices:
+ if isinstance(choice, litellm.Choices):
+ choice.message.content = "redacted-by-litellm"
+ elif isinstance(choice, litellm.utils.StreamingChoices):
+ choice.delta.content = "redacted-by-litellm"
+ elif isinstance(_result, litellm.ResponsesAPIResponse):
+ if hasattr(_result, "output"):
+ for output_item in _result.output:
+ if hasattr(output_item, "content") and isinstance(output_item.content, list):
+ for content_part in output_item.content:
+ if hasattr(content_part, "text"):
+ content_part.text = "redacted-by-litellm"
+ elif isinstance(_result, litellm.EmbeddingResponse):
+ if hasattr(_result, "data") and _result.data is not None:
+ _result.data = []
+ else:
+ return {"text": "redacted-by-litellm"}
return _result
- else:
- return {"text": "redacted-by-litellm"}
def should_redact_message_logging(model_call_details: dict) -> bool:
@@ -140,9 +156,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/streaming_chunk_builder_utils.py b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py
index 4068d2e043c..8f46f6b7141 100644
--- a/litellm/litellm_core_utils/streaming_chunk_builder_utils.py
+++ b/litellm/litellm_core_utils/streaming_chunk_builder_utils.py
@@ -1,6 +1,6 @@
import base64
import time
-from typing import Any, Dict, List, Optional, Union, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
from litellm.types.llms.openai import (
ChatCompletionAssistantContentValue,
@@ -16,11 +16,16 @@ from litellm.types.utils import (
FunctionCall,
ModelResponse,
ModelResponseStream,
- PromptTokensDetails,
+ PromptTokensDetailsWrapper,
Usage,
)
from litellm.utils import print_verbose, token_counter
+if TYPE_CHECKING:
+ from litellm.types.litellm_core_utils.streaming_chunk_builder_utils import (
+ UsagePerChunk,
+ )
+
class ChunkProcessor:
def __init__(self, chunks: List, messages: Optional[list] = None):
@@ -107,9 +112,9 @@ class ChunkProcessor:
self, tool_call_chunks: List[Dict[str, Any]]
) -> List[ChatCompletionMessageToolCall]:
tool_calls_list: List[ChatCompletionMessageToolCall] = []
- tool_call_map: Dict[
- int, Dict[str, Any]
- ] = {} # Map to store tool calls by index
+ tool_call_map: Dict[int, Dict[str, Any]] = (
+ {}
+ ) # Map to store tool calls by index
for chunk in tool_call_chunks:
choices = chunk["choices"]
@@ -256,7 +261,7 @@ class ChunkProcessor:
cache_creation_input_tokens: Optional[int] = None
cache_read_input_tokens: Optional[int] = None
completion_tokens_details: Optional[CompletionTokensDetails] = None
- prompt_tokens_details: Optional[PromptTokensDetails] = None
+ prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None
if "prompt_tokens" in usage_chunk:
prompt_tokens = usage_chunk.get("prompt_tokens", 0) or 0
@@ -277,10 +282,12 @@ class ChunkProcessor:
completion_tokens_details = usage_chunk.completion_tokens_details
if hasattr(usage_chunk, "prompt_tokens_details"):
if isinstance(usage_chunk.prompt_tokens_details, dict):
- prompt_tokens_details = PromptTokensDetails(
+ prompt_tokens_details = PromptTokensDetailsWrapper(
**usage_chunk.prompt_tokens_details
)
- elif isinstance(usage_chunk.prompt_tokens_details, PromptTokensDetails):
+ elif isinstance(
+ usage_chunk.prompt_tokens_details, PromptTokensDetailsWrapper
+ ):
prompt_tokens_details = usage_chunk.prompt_tokens_details
return {
@@ -306,26 +313,24 @@ class ChunkProcessor:
return reasoning_tokens
- def calculate_usage(
+ def _calculate_usage_per_chunk(
self,
chunks: List[Union[Dict[str, Any], ModelResponse]],
- model: str,
- completion_output: str,
- messages: Optional[List] = None,
- reasoning_tokens: Optional[int] = None,
- ) -> Usage:
- """
- Calculate usage for the given chunks.
- """
- returned_usage = Usage()
+ ) -> "UsagePerChunk":
+ from litellm.types.litellm_core_utils.streaming_chunk_builder_utils import (
+ UsagePerChunk,
+ )
+
# # Update usage information if needed
prompt_tokens = 0
completion_tokens = 0
## anthropic prompt caching information ##
cache_creation_input_tokens: Optional[int] = None
cache_read_input_tokens: Optional[int] = None
+
+ web_search_requests: Optional[int] = None
completion_tokens_details: Optional[CompletionTokensDetails] = None
- prompt_tokens_details: Optional[PromptTokensDetails] = None
+ prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = None
for chunk in chunks:
usage_chunk: Optional[Usage] = None
if "usage" in chunk:
@@ -366,7 +371,67 @@ class ChunkProcessor:
completion_tokens_details = usage_chunk_dict[
"completion_tokens_details"
]
+ if (
+ usage_chunk_dict["prompt_tokens_details"] is not None
+ and getattr(
+ usage_chunk_dict["prompt_tokens_details"],
+ "web_search_requests",
+ None,
+ )
+ is not None
+ ):
+ web_search_requests = getattr(
+ usage_chunk_dict["prompt_tokens_details"],
+ "web_search_requests",
+ )
+
prompt_tokens_details = usage_chunk_dict["prompt_tokens_details"]
+
+ return UsagePerChunk(
+ prompt_tokens=prompt_tokens,
+ completion_tokens=completion_tokens,
+ cache_creation_input_tokens=cache_creation_input_tokens,
+ cache_read_input_tokens=cache_read_input_tokens,
+ web_search_requests=web_search_requests,
+ completion_tokens_details=completion_tokens_details,
+ prompt_tokens_details=prompt_tokens_details,
+ )
+
+ def calculate_usage(
+ self,
+ chunks: List[Union[Dict[str, Any], ModelResponse]],
+ model: str,
+ completion_output: str,
+ messages: Optional[List] = None,
+ reasoning_tokens: Optional[int] = None,
+ ) -> Usage:
+ """
+ Calculate usage for the given chunks.
+ """
+ returned_usage = Usage()
+ # # Update usage information if needed
+
+ calculated_usage_per_chunk = self._calculate_usage_per_chunk(chunks=chunks)
+ prompt_tokens = calculated_usage_per_chunk["prompt_tokens"]
+ completion_tokens = calculated_usage_per_chunk["completion_tokens"]
+ ## anthropic prompt caching information ##
+ cache_creation_input_tokens: Optional[int] = calculated_usage_per_chunk[
+ "cache_creation_input_tokens"
+ ]
+ cache_read_input_tokens: Optional[int] = calculated_usage_per_chunk[
+ "cache_read_input_tokens"
+ ]
+
+ web_search_requests: Optional[int] = calculated_usage_per_chunk[
+ "web_search_requests"
+ ]
+ completion_tokens_details: Optional[CompletionTokensDetails] = (
+ calculated_usage_per_chunk["completion_tokens_details"]
+ )
+ prompt_tokens_details: Optional[PromptTokensDetailsWrapper] = (
+ calculated_usage_per_chunk["prompt_tokens_details"]
+ )
+
try:
returned_usage.prompt_tokens = prompt_tokens or token_counter(
model=model, messages=messages
@@ -415,6 +480,20 @@ class ChunkProcessor:
if prompt_tokens_details is not None:
returned_usage.prompt_tokens_details = prompt_tokens_details
+ if web_search_requests is not None:
+ if returned_usage.prompt_tokens_details is None:
+ returned_usage.prompt_tokens_details = PromptTokensDetailsWrapper(
+ web_search_requests=web_search_requests
+ )
+ else:
+ returned_usage.prompt_tokens_details.web_search_requests = (
+ web_search_requests
+ )
+
+ # Return a new usage object with the new values
+
+ returned_usage = Usage(**returned_usage.model_dump())
+
return returned_usage
diff --git a/litellm/litellm_core_utils/streaming_handler.py b/litellm/litellm_core_utils/streaming_handler.py
index 98fb94922f3..a8589cf8e4e 100644
--- a/litellm/litellm_core_utils/streaming_handler.py
+++ b/litellm/litellm_core_utils/streaming_handler.py
@@ -620,7 +620,6 @@ class CustomStreamWrapper:
args = {
"model": _model,
- "stream_options": self.stream_options,
**chunk_dict,
}
@@ -758,6 +757,7 @@ class CustomStreamWrapper:
is_chunk_non_empty = self.is_chunk_non_empty(
completion_obj, model_response, response_obj
)
+
if (
is_chunk_non_empty
): # cannot set content of an OpenAI Object to be an empty string
@@ -1203,6 +1203,9 @@ class CustomStreamWrapper:
if response_obj is None:
return
completion_obj["content"] = response_obj["text"]
+ self.intermittent_finish_reason = response_obj.get(
+ "finish_reason", None
+ )
if response_obj["is_finished"]:
if response_obj["finish_reason"] == "error":
raise Exception(
@@ -1561,6 +1564,7 @@ class CustomStreamWrapper:
complete_streaming_response = litellm.stream_chunk_builder(
chunks=self.chunks, messages=self.messages
)
+
response = self.model_response_creator()
if complete_streaming_response is not None:
setattr(
diff --git a/litellm/llms/anthropic/chat/handler.py b/litellm/llms/anthropic/chat/handler.py
index ffa0def9ce2..23175c9f2fa 100644
--- a/litellm/llms/anthropic/chat/handler.py
+++ b/litellm/llms/anthropic/chat/handler.py
@@ -515,9 +515,7 @@ class ModelResponseIterator:
usage_object=cast(dict, anthropic_usage_chunk), reasoning_content=None
)
- def _content_block_delta_helper(
- self, chunk: dict
- ) -> Tuple[
+ def _content_block_delta_helper(self, chunk: dict) -> Tuple[
str,
Optional[ChatCompletionToolCallChunk],
List[Union[ChatCompletionThinkingBlock, ChatCompletionRedactedThinkingBlock]],
@@ -600,6 +598,19 @@ class ModelResponseIterator:
return thinking_blocks, provider_specific_fields
+ def get_content_block_start(self, chunk: dict) -> ContentBlockStart:
+ from litellm.types.llms.anthropic import (
+ ContentBlockStartText,
+ ContentBlockStartToolUse,
+ )
+
+ if chunk.get("content_block", {}).get("type") == "tool_use":
+ content_block_start = ContentBlockStartToolUse(**chunk) # type: ignore
+ else:
+ content_block_start = ContentBlockStartText(**chunk) # type: ignore
+
+ return content_block_start
+
def chunk_parser(self, chunk: dict) -> ModelResponseStream:
try:
type_chunk = chunk.get("type", "") or ""
@@ -639,7 +650,8 @@ class ModelResponseIterator:
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01T1x1fJ34qAmk2tNTrN7Up6","name":"get_weather","input":{}}}
"""
- content_block_start = ContentBlockStart(**chunk) # type: ignore
+
+ content_block_start = self.get_content_block_start(chunk=chunk)
self.content_blocks = [] # reset content blocks when new block starts
if content_block_start["content_block"]["type"] == "text":
text = content_block_start["content_block"]["text"]
diff --git a/litellm/llms/anthropic/chat/transformation.py b/litellm/llms/anthropic/chat/transformation.py
index 6fe346167e2..8075d95bd18 100644
--- a/litellm/llms/anthropic/chat/transformation.py
+++ b/litellm/llms/anthropic/chat/transformation.py
@@ -709,7 +709,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
and isinstance(_litellm_metadata, dict)
and "user_id" in _litellm_metadata
and _litellm_metadata["user_id"] is not None
- and not _valid_user_id(_litellm_metadata["user_id"])
+ and _valid_user_id(_litellm_metadata["user_id"])
):
optional_params["metadata"] = {"user_id": _litellm_metadata["user_id"]}
diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py
index fad895a6253..5e0dfa9238a 100644
--- a/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py
+++ b/litellm/llms/anthropic/experimental_pass_through/adapters/handler.py
@@ -88,6 +88,9 @@ class LiteLLMMessagesToCompletionTransformationHandler:
if stream:
completion_kwargs["stream"] = stream
+ completion_kwargs["stream_options"] = {
+ "include_usage": True,
+ }
excluded_keys = {"anthropic_messages"}
extra_kwargs = extra_kwargs or {}
@@ -100,6 +103,9 @@ class LiteLLMMessagesToCompletionTransformationHandler:
from litellm.types.utils import CallTypes
setattr(value, "call_type", CallTypes.completion.value)
+ setattr(
+ value, "stream_options", completion_kwargs.get("stream_options")
+ )
if (
key not in excluded_keys
and key not in completion_kwargs
diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py
index 662c42f0d7e..aa95183bb6c 100644
--- a/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py
+++ b/litellm/llms/anthropic/experimental_pass_through/adapters/streaming_iterator.py
@@ -3,12 +3,16 @@
import json
import traceback
import uuid
-from typing import Any, AsyncIterator, Iterator, Optional
+from collections import deque
+from typing import TYPE_CHECKING, Any, AsyncIterator, Iterator, Literal, Optional
from litellm import verbose_logger
from litellm.types.llms.anthropic import UsageDelta
from litellm.types.utils import AdapterCompletionStreamWrapper
+if TYPE_CHECKING:
+ from litellm.types.utils import ModelResponseStream
+
class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
"""
@@ -17,6 +21,13 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
- finish_reason must map exactly to anthropic reason, else anthropic client won't be able to parse it.
"""
+ from litellm.types.llms.anthropic import (
+ ContentBlockContentBlockDict,
+ ContentBlockStart,
+ ContentBlockStartText,
+ TextBlock,
+ )
+
def __init__(self, completion_stream: Any, model: str):
super().__init__(completion_stream)
self.model = model
@@ -24,8 +35,17 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
sent_first_chunk: bool = False
sent_content_block_start: bool = False
sent_content_block_finish: bool = False
+ current_content_block_type: Literal["text", "tool_use"] = "text"
sent_last_message: bool = False
holding_chunk: Optional[Any] = None
+ holding_stop_reason_chunk: Optional[Any] = None
+ current_content_block_index: int = 0
+ current_content_block_start: ContentBlockContentBlockDict = TextBlock(
+ type="text",
+ text="",
+ )
+ pending_new_content_block: bool = False
+ chunk_queue: deque = deque() # Queue for buffering multiple chunks
def __next__(self):
from .transformation import LiteLLMAnthropicMessagesAdapter
@@ -50,17 +70,47 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
self.sent_content_block_start = True
return {
"type": "content_block_start",
- "index": 0,
+ "index": self.current_content_block_index,
"content_block": {"type": "text", "text": ""},
}
+ # Handle pending new content block start
+ if self.pending_new_content_block:
+ self.pending_new_content_block = False
+ self.sent_content_block_finish = False # Reset for new block
+ return {
+ "type": "content_block_start",
+ "index": self.current_content_block_index,
+ "content_block": self.current_content_block_start,
+ }
+
for chunk in self.completion_stream:
if chunk == "None" or chunk is None:
raise Exception
+ should_start_new_block = self._should_start_new_content_block(chunk)
+ if should_start_new_block:
+ self._increment_content_block_index()
+
processed_chunk = LiteLLMAnthropicMessagesAdapter().translate_streaming_openai_response_to_anthropic(
- response=chunk
+ response=chunk,
+ current_content_block_index=self.current_content_block_index,
)
+
+ # Check if we need to start a new content block
+ # This is where you'd add your logic to detect when a new content block should start
+ # For example, if the chunk indicates a tool call or different content type
+
+ if should_start_new_block and not self.sent_content_block_finish:
+ # End current content block and prepare for new one
+ self.holding_chunk = processed_chunk
+ self.sent_content_block_finish = True
+ self.pending_new_content_block = True
+ return {
+ "type": "content_block_stop",
+ "index": max(self.current_content_block_index - 1, 0),
+ }
+
if (
processed_chunk["type"] == "message_delta"
and self.sent_content_block_finish is False
@@ -69,7 +119,7 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
self.sent_content_block_finish = True
return {
"type": "content_block_stop",
- "index": 0,
+ "index": self.current_content_block_index,
}
elif self.holding_chunk is not None:
return_chunk = self.holding_chunk
@@ -96,64 +146,167 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
)
raise StopAsyncIteration
- async def __anext__(self):
+ async def __anext__(self): # noqa: PLR0915
from .transformation import LiteLLMAnthropicMessagesAdapter
try:
+ # Always return queued chunks first
+ if self.chunk_queue:
+ return self.chunk_queue.popleft()
+
+ # Queue initial chunks if not sent yet
if self.sent_first_chunk is False:
self.sent_first_chunk = True
- return {
- "type": "message_start",
- "message": {
- "id": "msg_{}".format(uuid.uuid4()),
- "type": "message",
- "role": "assistant",
- "content": [],
- "model": self.model,
- "stop_reason": None,
- "stop_sequence": None,
- "usage": UsageDelta(input_tokens=0, output_tokens=0),
- },
- }
+ self.chunk_queue.append(
+ {
+ "type": "message_start",
+ "message": {
+ "id": "msg_{}".format(uuid.uuid4()),
+ "type": "message",
+ "role": "assistant",
+ "content": [],
+ "model": self.model,
+ "stop_reason": None,
+ "stop_sequence": None,
+ "usage": UsageDelta(input_tokens=0, output_tokens=0),
+ },
+ }
+ )
+ return self.chunk_queue.popleft()
+
if self.sent_content_block_start is False:
self.sent_content_block_start = True
- return {
- "type": "content_block_start",
- "index": 0,
- "content_block": {"type": "text", "text": ""},
- }
+ self.chunk_queue.append(
+ {
+ "type": "content_block_start",
+ "index": self.current_content_block_index,
+ "content_block": {"type": "text", "text": ""},
+ }
+ )
+ return self.chunk_queue.popleft()
+
async for chunk in self.completion_stream:
if chunk == "None" or chunk is None:
raise Exception
+
+ # Check if we need to start a new content block
+ should_start_new_block = self._should_start_new_content_block(chunk)
+ if should_start_new_block:
+ self._increment_content_block_index()
+
processed_chunk = LiteLLMAnthropicMessagesAdapter().translate_streaming_openai_response_to_anthropic(
- response=chunk
+ response=chunk,
+ current_content_block_index=self.current_content_block_index,
)
+
+ # Check if this is a usage chunk and we have a held stop_reason chunk
+ if (
+ self.holding_stop_reason_chunk is not None
+ and getattr(chunk, "usage", None) is not None
+ ):
+ # Merge usage into the held stop_reason chunk
+ merged_chunk = self.holding_stop_reason_chunk.copy()
+ if "delta" not in merged_chunk:
+ merged_chunk["delta"] = {}
+
+ # Add usage to the held chunk
+ merged_chunk["usage"] = {
+ "input_tokens": chunk.usage.prompt_tokens or 0,
+ "output_tokens": chunk.usage.completion_tokens or 0,
+ }
+
+ # Queue the merged chunk and reset
+ self.chunk_queue.append(merged_chunk)
+ self.holding_stop_reason_chunk = None
+ return self.chunk_queue.popleft()
+
+ # Check if this processed chunk has a stop_reason - hold it for next chunk
+
+ if should_start_new_block and not self.sent_content_block_finish:
+ # Queue the sequence: content_block_stop -> content_block_start -> current_chunk
+
+ # 1. Stop current content block
+ self.chunk_queue.append(
+ {
+ "type": "content_block_stop",
+ "index": max(self.current_content_block_index - 1, 0),
+ }
+ )
+
+ # 2. Start new content block
+ self.chunk_queue.append(
+ {
+ "type": "content_block_start",
+ "index": self.current_content_block_index,
+ "content_block": self.current_content_block_start,
+ }
+ )
+
+ # 3. Queue the current chunk (don't lose it!)
+ self.chunk_queue.append(processed_chunk)
+
+ # Reset state for new block
+ self.sent_content_block_finish = False
+
+ # Return the first queued item
+ return self.chunk_queue.popleft()
+
if (
processed_chunk["type"] == "message_delta"
and self.sent_content_block_finish is False
):
- self.holding_chunk = processed_chunk
+ # Queue both the content_block_stop and the holding chunk
+ self.chunk_queue.append(
+ {
+ "type": "content_block_stop",
+ "index": self.current_content_block_index,
+ }
+ )
self.sent_content_block_finish = True
- return {
- "type": "content_block_stop",
- "index": 0,
- }
+ if processed_chunk.get("delta", {}).get("stop_reason") is not None:
+
+ self.holding_stop_reason_chunk = processed_chunk
+ else:
+ self.chunk_queue.append(processed_chunk)
+ return self.chunk_queue.popleft()
elif self.holding_chunk is not None:
- return_chunk = self.holding_chunk
- self.holding_chunk = processed_chunk
- return return_chunk
+ # Queue both chunks
+ self.chunk_queue.append(self.holding_chunk)
+ self.chunk_queue.append(processed_chunk)
+ self.holding_chunk = None
+ return self.chunk_queue.popleft()
else:
- return processed_chunk
+ # Queue the current chunk
+ self.chunk_queue.append(processed_chunk)
+ return self.chunk_queue.popleft()
+
+ # Handle any remaining held chunks after stream ends
+ if self.holding_stop_reason_chunk is not None:
+ self.chunk_queue.append(self.holding_stop_reason_chunk)
+ self.holding_stop_reason_chunk = None
+
if self.holding_chunk is not None:
- return_chunk = self.holding_chunk
+ self.chunk_queue.append(self.holding_chunk)
self.holding_chunk = None
- return return_chunk
- if self.sent_last_message is False:
+
+ if not self.sent_last_message:
self.sent_last_message = True
- return {"type": "message_stop"}
+ self.chunk_queue.append({"type": "message_stop"})
+
+ # Return queued items if any
+ if self.chunk_queue:
+ return self.chunk_queue.popleft()
+
raise StopIteration
+
except StopIteration:
- if self.sent_last_message is False:
+ # Handle any remaining queued chunks before stopping
+ if self.chunk_queue:
+ return self.chunk_queue.popleft()
+ # Handle any held stop_reason chunk
+ if self.holding_stop_reason_chunk is not None:
+ return self.holding_stop_reason_chunk
+ if not self.sent_last_message:
self.sent_last_message = True
return {"type": "message_stop"}
raise StopAsyncIteration
@@ -187,3 +340,37 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
else:
# For non-dict chunks, forward the original value unchanged
yield chunk
+
+ def _increment_content_block_index(self):
+ self.current_content_block_index += 1
+
+ def _should_start_new_content_block(self, chunk: "ModelResponseStream") -> bool:
+ """
+ Determine if we should start a new content block based on the processed chunk.
+ Override this method with your specific logic for detecting new content blocks.
+
+ Examples of when you might want to start a new content block:
+ - Switching from text to tool calls
+ - Different content types in the response
+ - Specific markers in the content
+ """
+ from .transformation import LiteLLMAnthropicMessagesAdapter
+
+ # Example logic - customize based on your needs:
+ # If chunk indicates a tool call
+ if chunk.choices[0].finish_reason is not None:
+ return False
+
+ (
+ block_type,
+ content_block_start,
+ ) = LiteLLMAnthropicMessagesAdapter()._translate_streaming_openai_chunk_to_anthropic_content_block(
+ choices=chunk.choices # type: ignore
+ )
+
+ if block_type != self.current_content_block_type:
+ self.current_content_block_type = block_type
+ self.current_content_block_start = content_block_start
+ return True
+
+ return False
diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py
index 369c668234f..5cf36a63f00 100644
--- a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py
+++ b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py
@@ -1,5 +1,15 @@
import json
-from typing import Any, AsyncIterator, List, Literal, Optional, Tuple, Union, cast
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ AsyncIterator,
+ List,
+ Literal,
+ Optional,
+ Tuple,
+ Union,
+ cast,
+)
from openai.types.chat.chat_completion_chunk import Choice as OpenAIStreamingChoice
@@ -45,6 +55,9 @@ from litellm.types.utils import Choices, ModelResponse, Usage
from .streaming_iterator import AnthropicStreamWrapper
+if TYPE_CHECKING:
+ from litellm.types.llms.anthropic import ContentBlockContentBlockDict
+
class AnthropicAdapter:
def __init__(self) -> None:
@@ -439,12 +452,40 @@ class LiteLLMAnthropicMessagesAdapter:
return translated_obj
+ def _translate_streaming_openai_chunk_to_anthropic_content_block(
+ self, choices: List[OpenAIStreamingChoice]
+ ) -> Tuple[
+ Literal["text", "tool_use"],
+ "ContentBlockContentBlockDict",
+ ]:
+ import uuid
+
+ from litellm.types.llms.anthropic import TextBlock, ToolUseBlock
+
+ for choice in choices:
+ if choice.delta.content is not None and len(choice.delta.content) > 0:
+ return "text", TextBlock(type="text", text="")
+ elif (
+ choice.delta.tool_calls is not None
+ and len(choice.delta.tool_calls) > 0
+ and choice.delta.tool_calls[0].function is not None
+ ):
+ return "tool_use", ToolUseBlock(
+ type="tool_use",
+ id=choice.delta.tool_calls[0].id or str(uuid.uuid4()),
+ name=choice.delta.tool_calls[0].function.name or "",
+ input={},
+ )
+
+ return "text", TextBlock(type="text", text="")
+
def _translate_streaming_openai_chunk_to_anthropic(
self, choices: List[OpenAIStreamingChoice]
) -> Tuple[
Literal["text_delta", "input_json_delta"],
Union[ContentTextBlockDelta, ContentJsonBlockDelta],
]:
+
text: str = ""
partial_json: Optional[str] = None
for choice in choices:
@@ -467,7 +508,7 @@ class LiteLLMAnthropicMessagesAdapter:
return "text_delta", ContentTextBlockDelta(type="text_delta", text=text)
def translate_streaming_openai_response_to_anthropic(
- self, response: ModelResponse
+ self, response: ModelResponse, current_content_block_index: int
) -> Union[ContentBlockDelta, MessageBlockDelta]:
## base case - final chunk w/ finish reason
if response.choices[0].finish_reason is not None:
@@ -503,6 +544,6 @@ class LiteLLMAnthropicMessagesAdapter:
)
return ContentBlockDelta(
type="content_block_delta",
- index=response.choices[0].index,
+ index=current_content_block_index,
delta=content_block_delta,
)
diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py
index 37fd839b3c5..58e40ce1b1b 100644
--- a/litellm/llms/anthropic/experimental_pass_through/messages/handler.py
+++ b/litellm/llms/anthropic/experimental_pass_through/messages/handler.py
@@ -17,6 +17,7 @@ from litellm.llms.base_llm.anthropic_messages.transformation import (
)
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler
from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
+from litellm.types.llms.anthropic_messages.anthropic_request import AnthropicMetadata
from litellm.types.llms.anthropic_messages.anthropic_response import (
AnthropicMessagesResponse,
)
@@ -91,6 +92,16 @@ async def anthropic_messages(
response = init_response
return response
+def validate_anthropic_api_metadata(metadata: Optional[Dict] = None) -> Optional[Dict]:
+ """
+ Validate Anthropic API metadata - This is done to ensure only allowed `metadata` fields are passed to Anthropic API
+
+ If there are any litellm specific metadata fields, use `litellm_metadata` key to pass them.
+ """
+ if metadata is None:
+ return None
+ anthropic_metadata_obj = AnthropicMetadata(**metadata)
+ return anthropic_metadata_obj.model_dump(exclude_none=True)
def anthropic_messages_handler(
max_tokens: int,
@@ -120,6 +131,7 @@ def anthropic_messages_handler(
Makes Anthropic `/v1/messages` API calls In the Anthropic API Spec
"""
from litellm.types.utils import LlmProviders
+ metadata = validate_anthropic_api_metadata(metadata)
local_vars = locals()
is_async = kwargs.pop("is_async", False)
diff --git a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py
index 160d4eafb57..46ba96f2605 100644
--- a/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py
+++ b/litellm/llms/anthropic/experimental_pass_through/messages/transformation.py
@@ -60,12 +60,17 @@ class AnthropicMessagesConfig(BaseAnthropicMessagesConfig):
api_key: Optional[str] = None,
api_base: Optional[str] = None,
) -> Tuple[dict, Optional[str]]:
+ import os
+
+ if api_key is None:
+ api_key = os.getenv("ANTHROPIC_API_KEY")
if "x-api-key" not in headers and api_key:
headers["x-api-key"] = api_key
if "anthropic-version" not in headers:
headers["anthropic-version"] = DEFAULT_ANTHROPIC_API_VERSION
if "content-type" not in headers:
headers["content-type"] = "application/json"
+
return headers, api_base
def transform_anthropic_messages_request(
diff --git a/litellm/llms/azure/responses/transformation.py b/litellm/llms/azure/responses/transformation.py
index e6f48179e49..e3d37c8a15a 100644
--- a/litellm/llms/azure/responses/transformation.py
+++ b/litellm/llms/azure/responses/transformation.py
@@ -20,8 +20,33 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
self, headers: dict, model: str, litellm_params: Optional[GenericLiteLLMParams]
) -> dict:
return BaseAzureLLM._base_validate_azure_environment(
- headers=headers,
- litellm_params=litellm_params
+ headers=headers, litellm_params=litellm_params
+ )
+
+ def get_stripped_model_name(self, model: str) -> str:
+ # if "responses/" is in the model name, remove it
+ if "responses/" in model:
+ model = model.replace("responses/", "")
+ if "o_series" in model:
+ model = model.replace("o_series/", "")
+ return model
+
+ def transform_responses_api_request(
+ self,
+ model: str,
+ input: Union[str, ResponseInputParam],
+ response_api_optional_request_params: Dict,
+ litellm_params: GenericLiteLLMParams,
+ headers: dict,
+ ) -> Dict:
+ """No transform applied since inputs are in OpenAI spec already"""
+ stripped_model_name = self.get_stripped_model_name(model)
+ return dict(
+ ResponsesAPIRequestParams(
+ model=stripped_model_name,
+ input=input,
+ **response_api_optional_request_params,
+ )
)
def get_complete_url(
@@ -46,11 +71,8 @@ class AzureOpenAIResponsesAPIConfig(OpenAIResponsesAPIConfig):
"https://litellm8397336933.openai.azure.com/openai/responses?api-version=2024-05-01-preview"
"""
return BaseAzureLLM._get_base_azure_url(
- api_base=api_base,
- litellm_params=litellm_params,
- route="/openai/responses"
+ api_base=api_base, litellm_params=litellm_params, route="/openai/responses"
)
-
#########################################################
########## DELETE RESPONSE API TRANSFORMATION ##############
diff --git a/litellm/llms/custom_httpx/http_handler.py b/litellm/llms/custom_httpx/http_handler.py
index 34968a63aee..cf2187153a9 100644
--- a/litellm/llms/custom_httpx/http_handler.py
+++ b/litellm/llms/custom_httpx/http_handler.py
@@ -4,6 +4,7 @@ import ssl
import time
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Union
+import certifi
import httpx
from aiohttp import ClientSession, TCPConnector
from httpx import USE_CLIENT_DEFAULT, AsyncHTTPTransport, HTTPTransport
@@ -39,6 +40,72 @@ headers = {
_DEFAULT_TIMEOUT = httpx.Timeout(timeout=5.0, connect=5.0)
+def get_ssl_configuration(ssl_verify: Optional[VerifyTypes] = None) -> Union[bool, str, ssl.SSLContext]:
+ """
+ Unified SSL configuration function that handles ssl_context and ssl_verify logic.
+
+ SSL Configuration Priority:
+ 1. If ssl_verify is provided -> is a SSL context use the custom SSL context
+ 2. If ssl_verify is False -> disable SSL verification (ssl=False)
+ 3. If ssl_verify is a string -> use it as a path to CA bundle file
+ 4. If SSL_CERT_FILE environment variable is set and exists -> use it as CA bundle file
+ 5. Else will use default SSL context with certifi CA bundle
+
+ If ssl_security_level is set, it will apply the security level to the SSL context.
+
+ Args:
+ ssl_verify: SSL verification setting. Can be:
+ - None: Use default from environment/litellm settings
+ - False: Disable SSL verification
+ - True: Enable SSL verification
+ - str: Path to CA bundle file
+
+ Returns:
+ Union[bool, str, ssl.SSLContext]: Appropriate SSL configuration
+ """
+ from litellm.secret_managers.main import str_to_bool
+
+ if isinstance(ssl_verify, ssl.SSLContext):
+ # If ssl_verify is already an SSLContext, return it directly
+ return ssl_verify
+
+ # Get ssl_verify from environment or litellm settings if not provided
+ if ssl_verify is None:
+ ssl_verify = os.getenv("SSL_VERIFY", litellm.ssl_verify)
+ ssl_verify_bool = str_to_bool(ssl_verify) if isinstance(ssl_verify, str) else ssl_verify
+ if ssl_verify_bool is not None:
+ ssl_verify = ssl_verify_bool
+
+ ssl_security_level = os.getenv("SSL_SECURITY_LEVEL", litellm.ssl_security_level)
+
+ cafile = None
+ if isinstance(ssl_verify, str) and os.path.exists(ssl_verify):
+ cafile = ssl_verify
+ if not cafile:
+ ssl_cert_file = os.getenv("SSL_CERT_FILE")
+ if ssl_cert_file and os.path.exists(ssl_cert_file):
+ cafile = ssl_cert_file
+ else:
+ cafile = certifi.where()
+
+ if ssl_verify is not False:
+ custom_ssl_context = ssl.create_default_context(
+ cafile=cafile
+ )
+ # If security level is set, apply it to the SSL context
+ if (
+ ssl_security_level
+ and isinstance(ssl_security_level, str)
+ ):
+ # Create a custom SSL context with reduced security level
+ custom_ssl_context.set_ciphers(ssl_security_level)
+
+ # Use our custom SSL context instead of the original ssl_verify value
+ return custom_ssl_context
+
+ return ssl_verify
+
+
def mask_sensitive_info(error_message):
# Find the start of the key parameter
if isinstance(error_message, str):
@@ -119,29 +186,8 @@ 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:
- ssl_verify = os.getenv("SSL_VERIFY", litellm.ssl_verify)
-
- ssl_security_level = os.getenv("SSL_SECURITY_LEVEL")
-
- # If ssl_verify is not False and we need a lower security level
- if (
- not ssl_verify
- and ssl_security_level
- and isinstance(ssl_security_level, str)
- ):
- # Create a custom SSL context with reduced security level
- custom_ssl_context = ssl.create_default_context()
- custom_ssl_context.set_ciphers(ssl_security_level)
-
- # If ssl_verify is a path to a CA bundle, load it into our custom context
- if isinstance(ssl_verify, str) and os.path.exists(ssl_verify):
- custom_ssl_context.load_verify_locations(cafile=ssl_verify)
-
- # Use our custom SSL context instead of the original ssl_verify value
- ssl_verify = custom_ssl_context
+ # Get unified SSL configuration
+ ssl_config = get_ssl_configuration(ssl_verify)
# An SSL certificate used by the requested host to authenticate the client.
# /path/to/client.pem
@@ -152,8 +198,8 @@ class AsyncHTTPHandler:
# Create a client with a connection pool
transport = AsyncHTTPHandler._create_async_transport(
- ssl_context=ssl_verify if isinstance(ssl_verify, ssl.SSLContext) else None,
- ssl_verify=ssl_verify if isinstance(ssl_verify, bool) else None,
+ ssl_context=ssl_config if isinstance(ssl_config, ssl.SSLContext) else None,
+ ssl_verify=ssl_config if isinstance(ssl_config, bool) else None,
)
return httpx.AsyncClient(
@@ -164,7 +210,7 @@ class AsyncHTTPHandler:
max_connections=concurrent_limit,
max_keepalive_connections=concurrent_limit,
),
- verify=ssl_verify,
+ verify=ssl_config,
cert=cert,
headers=headers,
)
@@ -544,7 +590,6 @@ class AsyncHTTPHandler:
SSL Configuration Priority:
1. If ssl_context is provided -> use the custom SSL context
2. If ssl_verify is False -> disable SSL verification (ssl=False)
- 3. If ssl_verify is True/None -> use default SSL context with certifi CA bundle
Returns:
Dict with appropriate SSL configuration for TCPConnector
@@ -559,10 +604,6 @@ class AsyncHTTPHandler:
elif ssl_verify is False:
# Priority 2: Explicitly disable SSL verification
connector_kwargs["verify_ssl"] = False
- else:
- # Priority 3: Use our default SSL context with certifi CA bundle
- # This covers ssl_verify=True and ssl_verify=None cases
- connector_kwargs["ssl"] = AsyncHTTPHandler._get_ssl_context()
return connector_kwargs
@@ -577,7 +618,6 @@ class AsyncHTTPHandler:
Note: aiohttp TCPConnector ssl parameter accepts:
- SSLContext: custom SSL context
- False: disable SSL verification
- - True: use default SSL verification (equivalent to ssl.create_default_context())
"""
from litellm.llms.custom_httpx.aiohttp_transport import LiteLLMAiohttpTransport
from litellm.secret_managers.main import str_to_bool
@@ -600,17 +640,6 @@ class AsyncHTTPHandler:
trust_env=trust_env,
),
)
-
-
- @staticmethod
- def _get_ssl_context() -> ssl.SSLContext:
- """
- Get the SSL context for the AiohttpTransport
- """
- import certifi
- return ssl.create_default_context(
- cafile=certifi.where()
- )
@staticmethod
def _create_httpx_transport() -> Optional[AsyncHTTPTransport]:
@@ -637,11 +666,8 @@ class HTTPHandler:
if timeout is None:
timeout = _DEFAULT_TIMEOUT
- # SSL certificates (a.k.a CA bundle) used to verify the identity of requested hosts.
- # /path/to/certificate.pem
-
- if ssl_verify is None:
- ssl_verify = os.getenv("SSL_VERIFY", litellm.ssl_verify)
+ # Get unified SSL configuration
+ ssl_config = get_ssl_configuration(ssl_verify)
# An SSL certificate used by the requested host to authenticate the client.
# /path/to/client.pem
@@ -658,7 +684,7 @@ class HTTPHandler:
max_connections=concurrent_limit,
max_keepalive_connections=concurrent_limit,
),
- verify=ssl_verify,
+ verify=ssl_config,
cert=cert,
headers=headers,
)
diff --git a/litellm/llms/custom_httpx/llm_http_handler.py b/litellm/llms/custom_httpx/llm_http_handler.py
index 75013aea83c..e08b909b2a9 100644
--- a/litellm/llms/custom_httpx/llm_http_handler.py
+++ b/litellm/llms/custom_httpx/llm_http_handler.py
@@ -1008,12 +1008,12 @@ class BaseLLMHTTPHandler:
"""
Shared logic for preparing audio transcription requests.
Returns: (headers, complete_url, data, files)
- """
+ """
# Handle the response based on type
from litellm.llms.base_llm.audio_transcription.transformation import (
AudioTranscriptionRequestData,
)
-
+
headers = provider_config.validate_environment(
api_key=api_key,
headers=headers or {},
@@ -1038,11 +1038,13 @@ class BaseLLMHTTPHandler:
optional_params=optional_params,
litellm_params=litellm_params,
)
-
+
# All providers now return AudioTranscriptionRequestData
if not isinstance(transformed_result, AudioTranscriptionRequestData):
- raise ValueError(f"Provider {provider_config.__class__.__name__} must return AudioTranscriptionRequestData")
-
+ raise ValueError(
+ f"Provider {provider_config.__class__.__name__} must return AudioTranscriptionRequestData"
+ )
+
data = transformed_result.data
files = transformed_result.files
@@ -1143,7 +1145,9 @@ class BaseLLMHTTPHandler:
headers=headers,
data=data,
files=files,
- json=data if files is None and isinstance(data, dict) else None, # Use json param only when no files and data is dict
+ json=(
+ data if files is None and isinstance(data, dict) else None
+ ), # Use json param only when no files and data is dict
timeout=timeout,
)
except Exception as e:
@@ -1214,7 +1218,9 @@ class BaseLLMHTTPHandler:
headers=headers,
data=data,
files=files,
- json=data if files is None and isinstance(data, dict) else None, # Use json param only when no files and data is dict
+ json=(
+ data if files is None and isinstance(data, dict) else None
+ ), # Use json param only when no files and data is dict
timeout=timeout,
)
except Exception as e:
@@ -1432,6 +1438,7 @@ class BaseLLMHTTPHandler:
Handles responses API requests.
When _is_async=True, returns a coroutine instead of making the call directly.
"""
+
if _is_async:
# Return the async coroutine if called with _is_async=True
return self.async_response_api_handler(
diff --git a/litellm/llms/gemini/common_utils.py b/litellm/llms/gemini/common_utils.py
index 3331f584b51..31b57434e10 100644
--- a/litellm/llms/gemini/common_utils.py
+++ b/litellm/llms/gemini/common_utils.py
@@ -50,6 +50,14 @@ class GeminiModelInfo(BaseLLMModelInfo):
def get_base_model(model: str) -> Optional[str]:
return model.replace("gemini/", "")
+ def process_model_name(self, models: List[Dict[str, str]]) -> List[str]:
+ litellm_model_names = []
+ for model in models:
+ stripped_model_name = model["name"].replace("models/", "")
+ litellm_model_name = "gemini/" + stripped_model_name
+ litellm_model_names.append(litellm_model_name)
+ return litellm_model_names
+
def get_models(
self, api_key: Optional[str] = None, api_base: Optional[str] = None
) -> List[str]:
@@ -72,11 +80,7 @@ class GeminiModelInfo(BaseLLMModelInfo):
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)
+ litellm_model_names = self.process_model_name(models)
return litellm_model_names
def get_error_class(
diff --git a/litellm/llms/github_copilot/authenticator.py b/litellm/llms/github_copilot/authenticator.py
new file mode 100644
index 00000000000..fdf59a9a21c
--- /dev/null
+++ b/litellm/llms/github_copilot/authenticator.py
@@ -0,0 +1,345 @@
+import json
+import os
+import time
+from datetime import datetime
+from typing import Any, Dict, Optional
+
+import httpx
+
+from litellm._logging import verbose_logger
+from litellm.llms.custom_httpx.http_handler import _get_httpx_client
+
+from .common_utils import (
+ APIKeyExpiredError,
+ GetAccessTokenError,
+ GetAPIKeyError,
+ GetDeviceCodeError,
+ RefreshAPIKeyError,
+)
+
+# Constants
+GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98"
+GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code"
+GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"
+GITHUB_API_KEY_URL = "https://api.github.com/copilot_internal/v2/token"
+
+
+class Authenticator:
+ def __init__(self) -> None:
+ """Initialize the GitHub Copilot authenticator with configurable token paths."""
+ # Token storage paths
+ self.token_dir = os.getenv(
+ "GITHUB_COPILOT_TOKEN_DIR",
+ os.path.expanduser("~/.config/litellm/github_copilot"),
+ )
+ self.access_token_file = os.path.join(
+ self.token_dir,
+ os.getenv("GITHUB_COPILOT_ACCESS_TOKEN_FILE", "access-token"),
+ )
+ self.api_key_file = os.path.join(
+ self.token_dir, os.getenv("GITHUB_COPILOT_API_KEY_FILE", "api-key.json")
+ )
+ self._ensure_token_dir()
+
+ def get_access_token(self) -> str:
+ """
+ Login to Copilot with retry 3 times.
+
+ Returns:
+ str: The GitHub access token.
+
+ Raises:
+ GetAccessTokenError: If unable to obtain an access token after retries.
+ """
+ try:
+ with open(self.access_token_file, "r") as f:
+ access_token = f.read().strip()
+ if access_token:
+ return access_token
+ except IOError:
+ verbose_logger.warning(
+ "No existing access token found or error reading file"
+ )
+
+ for attempt in range(3):
+ verbose_logger.debug(f"Access token acquisition attempt {attempt + 1}/3")
+ try:
+ access_token = self._login()
+ try:
+ with open(self.access_token_file, "w") as f:
+ f.write(access_token)
+ except IOError:
+ verbose_logger.error("Error saving access token to file")
+ return access_token
+ except (GetDeviceCodeError, GetAccessTokenError, RefreshAPIKeyError) as e:
+ verbose_logger.warning(f"Failed attempt {attempt + 1}: {str(e)}")
+ continue
+
+ raise GetAccessTokenError(
+ message="Failed to get access token after 3 attempts",
+ status_code=401,
+ )
+
+ def get_api_key(self) -> str:
+ """
+ Get the API key, refreshing if necessary.
+
+ Returns:
+ str: The GitHub Copilot API key.
+
+ Raises:
+ GetAPIKeyError: If unable to obtain an API key.
+ """
+ try:
+ with open(self.api_key_file, "r") as f:
+ api_key_info = json.load(f)
+ if api_key_info.get("expires_at", 0) > datetime.now().timestamp():
+ return api_key_info.get("token")
+ else:
+ verbose_logger.warning("API key expired, refreshing")
+ raise APIKeyExpiredError(
+ message="API key expired",
+ status_code=401,
+ )
+ except IOError:
+ verbose_logger.warning("No API key file found or error opening file")
+ except (json.JSONDecodeError, KeyError) as e:
+ verbose_logger.warning(f"Error reading API key from file: {str(e)}")
+ except APIKeyExpiredError:
+ pass # Already logged in the try block
+
+ try:
+ api_key_info = self._refresh_api_key()
+ with open(self.api_key_file, "w") as f:
+ json.dump(api_key_info, f)
+ token = api_key_info.get("token")
+ if token:
+ return token
+ else:
+ raise GetAPIKeyError(
+ message="API key response missing token",
+ status_code=401,
+ )
+ except IOError as e:
+ verbose_logger.error(f"Error saving API key to file: {str(e)}")
+ raise GetAPIKeyError(
+ message=f"Failed to save API key: {str(e)}",
+ status_code=500,
+ )
+ except RefreshAPIKeyError as e:
+ raise GetAPIKeyError(
+ message=f"Failed to refresh API key: {str(e)}",
+ status_code=401,
+ )
+
+ def _refresh_api_key(self) -> Dict[str, Any]:
+ """
+ Refresh the API key using the access token.
+
+ Returns:
+ Dict[str, Any]: The API key information including token and expiration.
+
+ Raises:
+ RefreshAPIKeyError: If unable to refresh the API key.
+ """
+ access_token = self.get_access_token()
+ headers = self._get_github_headers(access_token)
+
+ max_retries = 3
+ for attempt in range(max_retries):
+ try:
+ sync_client = _get_httpx_client()
+ response = sync_client.get(GITHUB_API_KEY_URL, headers=headers)
+ response.raise_for_status()
+
+ response_json = response.json()
+
+ if "token" in response_json:
+ return response_json
+ else:
+ verbose_logger.warning(
+ f"API key response missing token: {response_json}"
+ )
+ except httpx.HTTPStatusError as e:
+ verbose_logger.error(
+ f"HTTP error refreshing API key (attempt {attempt+1}/{max_retries}): {str(e)}"
+ )
+ except Exception as e:
+ verbose_logger.error(f"Unexpected error refreshing API key: {str(e)}")
+
+ raise RefreshAPIKeyError(
+ message="Failed to refresh API key after maximum retries",
+ status_code=401,
+ )
+
+ def _ensure_token_dir(self) -> None:
+ """Ensure the token directory exists."""
+ if not os.path.exists(self.token_dir):
+ os.makedirs(self.token_dir, exist_ok=True)
+
+ def _get_github_headers(self, access_token: Optional[str] = None) -> Dict[str, str]:
+ """
+ Generate standard GitHub headers for API requests.
+
+ Args:
+ access_token: Optional access token to include in the headers.
+
+ Returns:
+ Dict[str, str]: Headers for GitHub API requests.
+ """
+ headers = {
+ "accept": "application/json",
+ "editor-version": "vscode/1.85.1",
+ "editor-plugin-version": "copilot/1.155.0",
+ "user-agent": "GithubCopilot/1.155.0",
+ "accept-encoding": "gzip,deflate,br",
+ }
+
+ if access_token:
+ headers["authorization"] = f"token {access_token}"
+
+ if "content-type" not in headers:
+ headers["content-type"] = "application/json"
+
+ return headers
+
+ def _get_device_code(self) -> Dict[str, str]:
+ """
+ Get a device code for GitHub authentication.
+
+ Returns:
+ Dict[str, str]: Device code information.
+
+ Raises:
+ GetDeviceCodeError: If unable to get a device code.
+ """
+ try:
+ sync_client = _get_httpx_client()
+ resp = sync_client.post(
+ GITHUB_DEVICE_CODE_URL,
+ headers=self._get_github_headers(),
+ json={"client_id": GITHUB_CLIENT_ID, "scope": "read:user"},
+ )
+ resp.raise_for_status()
+ resp_json = resp.json()
+
+ required_fields = ["device_code", "user_code", "verification_uri"]
+ if not all(field in resp_json for field in required_fields):
+ verbose_logger.error(f"Response missing required fields: {resp_json}")
+ raise GetDeviceCodeError(
+ message="Response missing required fields",
+ status_code=400,
+ )
+
+ return resp_json
+ except httpx.HTTPStatusError as e:
+ verbose_logger.error(f"HTTP error getting device code: {str(e)}")
+ raise GetDeviceCodeError(
+ message=f"Failed to get device code: {str(e)}",
+ status_code=400,
+ )
+ except json.JSONDecodeError as e:
+ verbose_logger.error(f"Error decoding JSON response: {str(e)}")
+ raise GetDeviceCodeError(
+ message=f"Failed to decode device code response: {str(e)}",
+ status_code=400,
+ )
+ except Exception as e:
+ verbose_logger.error(f"Unexpected error getting device code: {str(e)}")
+ raise GetDeviceCodeError(
+ message=f"Failed to get device code: {str(e)}",
+ status_code=400,
+ )
+
+ def _poll_for_access_token(self, device_code: str) -> str:
+ """
+ Poll for an access token after user authentication.
+
+ Args:
+ device_code: The device code to use for polling.
+
+ Returns:
+ str: The access token.
+
+ Raises:
+ GetAccessTokenError: If unable to get an access token.
+ """
+ sync_client = _get_httpx_client()
+ max_attempts = 12 # 1 minute (12 * 5 seconds)
+
+ for attempt in range(max_attempts):
+ try:
+ resp = sync_client.post(
+ GITHUB_ACCESS_TOKEN_URL,
+ headers=self._get_github_headers(),
+ json={
+ "client_id": GITHUB_CLIENT_ID,
+ "device_code": device_code,
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
+ },
+ )
+ resp.raise_for_status()
+ resp_json = resp.json()
+
+ if "access_token" in resp_json:
+ verbose_logger.info("Authentication successful!")
+ return resp_json["access_token"]
+ elif (
+ "error" in resp_json
+ and resp_json.get("error") == "authorization_pending"
+ ):
+ verbose_logger.debug(
+ f"Authorization pending (attempt {attempt+1}/{max_attempts})"
+ )
+ else:
+ verbose_logger.warning(f"Unexpected response: {resp_json}")
+ except httpx.HTTPStatusError as e:
+ verbose_logger.error(f"HTTP error polling for access token: {str(e)}")
+ raise GetAccessTokenError(
+ message=f"Failed to get access token: {str(e)}",
+ status_code=400,
+ )
+ except json.JSONDecodeError as e:
+ verbose_logger.error(f"Error decoding JSON response: {str(e)}")
+ raise GetAccessTokenError(
+ message=f"Failed to decode access token response: {str(e)}",
+ status_code=400,
+ )
+ except Exception as e:
+ verbose_logger.error(
+ f"Unexpected error polling for access token: {str(e)}"
+ )
+ raise GetAccessTokenError(
+ message=f"Failed to get access token: {str(e)}",
+ status_code=400,
+ )
+
+ time.sleep(5)
+
+ raise GetAccessTokenError(
+ message="Timed out waiting for user to authorize the device",
+ status_code=400,
+ )
+
+ def _login(self) -> str:
+ """
+ Login to GitHub Copilot using device code flow.
+
+ Returns:
+ str: The GitHub access token.
+
+ Raises:
+ GetDeviceCodeError: If unable to get a device code.
+ GetAccessTokenError: If unable to get an access token.
+ """
+ device_code_info = self._get_device_code()
+
+ device_code = device_code_info["device_code"]
+ user_code = device_code_info["user_code"]
+ verification_uri = device_code_info["verification_uri"]
+
+ print( # noqa: T201
+ f"Please visit {verification_uri} and enter code {user_code} to authenticate."
+ )
+
+ return self._poll_for_access_token(device_code)
diff --git a/litellm/llms/github_copilot/chat/transformation.py b/litellm/llms/github_copilot/chat/transformation.py
new file mode 100644
index 00000000000..395eb81c5ba
--- /dev/null
+++ b/litellm/llms/github_copilot/chat/transformation.py
@@ -0,0 +1,37 @@
+from typing import Optional, Tuple
+
+from litellm.exceptions import AuthenticationError
+from litellm.llms.openai.openai import OpenAIConfig
+
+from ..authenticator import Authenticator
+from ..common_utils import GetAPIKeyError
+
+
+class GithubCopilotConfig(OpenAIConfig):
+ GITHUB_COPILOT_API_BASE = "https://api.github.com/copilot/v1"
+ def __init__(
+ self,
+ api_key: Optional[str] = None,
+ api_base: Optional[str] = None,
+ custom_llm_provider: str = "openai",
+ ) -> None:
+ super().__init__()
+ self.authenticator = Authenticator()
+
+ def _get_openai_compatible_provider_info(
+ self,
+ model: str,
+ api_base: Optional[str],
+ api_key: Optional[str],
+ custom_llm_provider: str,
+ ) -> Tuple[Optional[str], Optional[str], str]:
+ api_base = self.GITHUB_COPILOT_API_BASE
+ try:
+ dynamic_api_key = self.authenticator.get_api_key()
+ except GetAPIKeyError as e:
+ raise AuthenticationError(
+ model=model,
+ llm_provider=custom_llm_provider,
+ message=str(e),
+ )
+ return api_base, dynamic_api_key, custom_llm_provider
diff --git a/litellm/llms/github_copilot/common_utils.py b/litellm/llms/github_copilot/common_utils.py
new file mode 100644
index 00000000000..4c9a4b6dad0
--- /dev/null
+++ b/litellm/llms/github_copilot/common_utils.py
@@ -0,0 +1,49 @@
+"""
+Constants for Copilot integration
+"""
+from typing import Optional, Union
+
+import httpx
+
+from litellm.llms.base_llm.chat.transformation import BaseLLMException
+
+
+class GithubCopilotError(BaseLLMException):
+ def __init__(
+ self,
+ status_code,
+ message,
+ request: Optional[httpx.Request] = None,
+ response: Optional[httpx.Response] = None,
+ headers: Optional[Union[httpx.Headers, dict]] = None,
+ body: Optional[dict] = None,
+ ):
+ super().__init__(
+ status_code=status_code,
+ message=message,
+ request=request,
+ response=response,
+ headers=headers,
+ body=body,
+ )
+
+
+
+class GetDeviceCodeError(GithubCopilotError):
+ pass
+
+
+class GetAccessTokenError(GithubCopilotError):
+ pass
+
+
+class APIKeyExpiredError(GithubCopilotError):
+ pass
+
+
+class RefreshAPIKeyError(GithubCopilotError):
+ pass
+
+
+class GetAPIKeyError(GithubCopilotError):
+ pass
diff --git a/litellm/llms/huggingface/chat/transformation.py b/litellm/llms/huggingface/chat/transformation.py
index 03ae2a52ac3..557aa48550b 100644
--- a/litellm/llms/huggingface/chat/transformation.py
+++ b/litellm/llms/huggingface/chat/transformation.py
@@ -109,22 +109,19 @@ class HuggingFaceChatConfig(OpenAIGPTConfig):
# Default construction with provider
else:
# Parse provider and model
+ complete_url = "https://router.huggingface.co/v1/chat/completions"
first_part, remaining = model.split("/", 1)
if "/" in remaining:
provider = first_part
- else:
- provider = "hf-inference"
-
- if provider == "hf-inference":
- route = f"{provider}/models/{model}/v1/chat/completions"
- elif provider == "novita":
- route = f"{provider}/v3/openai/chat/completions"
- elif provider == "fireworks-ai":
- route = f"{provider}/inference/v1/chat/completions"
- else:
- route = f"{provider}/v1/chat/completions"
- complete_url = f"{BASE_URL}/{route}"
-
+ if provider == "hf-inference":
+ route = f"{provider}/models/{model}/v1/chat/completions"
+ elif provider == "novita":
+ route = f"{provider}/v3/openai/chat/completions"
+ elif provider == "fireworks-ai":
+ route = f"{provider}/inference/v1/chat/completions"
+ else:
+ route = f"{provider}/v1/chat/completions"
+ complete_url = f"{BASE_URL}/{route}"
# Ensure URL doesn't end with a slash
complete_url = complete_url.rstrip("/")
return complete_url
@@ -145,25 +142,24 @@ class HuggingFaceChatConfig(OpenAIGPTConfig):
logger.warning("`max_retries` is not supported. It will be ignored.")
optional_params.pop("max_retries", None)
first_part, remaining = model.split("/", 1)
+ mapped_model = model
if "/" in remaining:
provider = first_part
model_id = remaining
- else:
- provider = "hf-inference"
- model_id = model
- provider_mapping = _fetch_inference_provider_mapping(model_id)
- if provider not in provider_mapping:
- raise HuggingFaceError(
- message=f"Model {model_id} is not supported for provider {provider}",
- status_code=404,
- headers={},
- )
- provider_mapping = provider_mapping[provider]
- if provider_mapping["status"] == "staging":
- logger.warning(
- f"Model {model_id} is in staging mode for provider {provider}. Meant for test purposes only."
- )
- mapped_model = provider_mapping["providerId"]
+ provider_mapping = _fetch_inference_provider_mapping(model_id)
+ if provider not in provider_mapping:
+ raise HuggingFaceError(
+ message=f"Model {model_id} is not supported for provider {provider}",
+ status_code=404,
+ headers={},
+ )
+ provider_mapping = provider_mapping[provider]
+ if provider_mapping["status"] == "staging":
+ logger.warning(
+ f"Model {model_id} is in staging mode for provider {provider}. Meant for test purposes only."
+ )
+ mapped_model = provider_mapping["providerId"]
+
messages = self._transform_messages(messages=messages, model=mapped_model)
return dict(
ChatCompletionRequest(
diff --git a/litellm/llms/mistral/chat.py b/litellm/llms/mistral/chat.py
deleted file mode 100644
index fc454038f1c..00000000000
--- a/litellm/llms/mistral/chat.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""
-Calls handled in openai/
-
-as mistral is an openai-compatible endpoint.
-"""
diff --git a/litellm/llms/mistral/mistral_chat_transformation.py b/litellm/llms/mistral/chat/transformation.py
similarity index 82%
rename from litellm/llms/mistral/mistral_chat_transformation.py
rename to litellm/llms/mistral/chat/transformation.py
index e281e055537..0441e75beec 100644
--- a/litellm/llms/mistral/mistral_chat_transformation.py
+++ b/litellm/llms/mistral/chat/transformation.py
@@ -8,6 +8,8 @@ Docs - https://docs.mistral.ai/api/
from typing import Any, Coroutine, List, Literal, Optional, Tuple, Union, cast, overload
+import httpx
+from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
from litellm.litellm_core_utils.prompt_templates.common_utils import (
handle_messages_with_content_list_to_str_conversion,
strip_none_values_from_message,
@@ -16,6 +18,8 @@ from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
from litellm.secret_managers.main import get_secret_str
from litellm.types.llms.mistral import MistralToolCallMessage
from litellm.types.llms.openai import AllMessageValues
+from litellm.types.utils import ModelResponse
+from litellm.utils import convert_to_model_response_object
class MistralConfig(OpenAIGPTConfig):
@@ -92,7 +96,7 @@ class MistralConfig(OpenAIGPTConfig):
# Add reasoning support for magistral models
if "magistral" in model.lower():
supported_params.extend(["thinking", "reasoning_effort"])
-
+
return supported_params
def _map_tool_choice(self, tool_choice: str) -> str:
@@ -140,9 +144,7 @@ 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
+ if param == "max_completion_tokens": # max_completion_tokens should take priority
optional_params["max_tokens"] = value
if param == "tools":
optional_params["tools"] = value
@@ -155,9 +157,7 @@ class MistralConfig(OpenAIGPTConfig):
if param == "stop":
optional_params["stop"] = value
if param == "tool_choice" and isinstance(value, str):
- optional_params["tool_choice"] = self._map_tool_choice(
- tool_choice=value
- )
+ optional_params["tool_choice"] = self._map_tool_choice(tool_choice=value)
if param == "seed":
optional_params["extra_body"] = {"random_seed": value}
if param == "response_format":
@@ -174,7 +174,7 @@ class MistralConfig(OpenAIGPTConfig):
def _get_openai_compatible_provider_info(
self, api_base: Optional[str], api_key: Optional[str]
- ) -> Tuple[Optional[str], Optional[str]]:
+ ) -> Tuple[str, Optional[str]]:
# mistral is openai compatible, we just need to set this to custom_openai and have the api_base be https://api.mistral.ai
api_base = (
api_base
@@ -183,9 +183,7 @@ class MistralConfig(OpenAIGPTConfig):
) # type: ignore
# if api_base does not end with /v1 we add it
- if api_base is not None and not api_base.endswith(
- "/v1"
- ): # Mistral always needs a /v1 at the end
+ if api_base is not None and not api_base.endswith("/v1"): # Mistral always needs a /v1 at the end
api_base = api_base + "/v1"
dynamic_api_key = (
api_key
@@ -197,8 +195,7 @@ class MistralConfig(OpenAIGPTConfig):
@overload
def _transform_messages(
self, messages: List[AllMessageValues], model: str, is_async: Literal[True]
- ) -> Coroutine[Any, Any, List[AllMessageValues]]:
- ...
+ ) -> Coroutine[Any, Any, List[AllMessageValues]]: ...
@overload
def _transform_messages(
@@ -206,8 +203,7 @@ class MistralConfig(OpenAIGPTConfig):
messages: List[AllMessageValues],
model: str,
is_async: Literal[False] = False,
- ) -> List[AllMessageValues]:
- ...
+ ) -> List[AllMessageValues]: ...
def _transform_messages(
self, messages: List[AllMessageValues], model: str, is_async: bool = False
@@ -248,52 +244,44 @@ class MistralConfig(OpenAIGPTConfig):
return super()._transform_messages(new_messages, model, False)
def _add_reasoning_system_prompt_if_needed(
- self,
- messages: List[AllMessageValues],
- optional_params: dict
+ self, messages: List[AllMessageValues], optional_params: dict
) -> List[AllMessageValues]:
"""
Add reasoning system prompt for Mistral magistral models when reasoning_effort is specified.
"""
if not optional_params.get("_add_reasoning_prompt", False):
return messages
-
+
# Check if there's already a system message
has_system_message = any(msg.get("role") == "system" for msg in messages)
-
+
if has_system_message:
# Prepend reasoning instructions to existing system message
for i, msg in enumerate(messages):
if msg.get("role") == "system":
existing_content = msg.get("content", "")
reasoning_prompt = self._get_mistral_reasoning_system_prompt()
-
+
# Handle both string and list content, preserving original format
if isinstance(existing_content, str):
# String content - prepend reasoning prompt
new_content: Union[str, list] = f"{reasoning_prompt}\n\n{existing_content}"
elif isinstance(existing_content, list):
# List content - prepend reasoning prompt as text block
- new_content = [
- {"type": "text", "text": reasoning_prompt + "\n\n"}
- ] + existing_content
+ new_content = [{"type": "text", "text": reasoning_prompt + "\n\n"}] + existing_content
else:
# Fallback for any other type - convert to string
new_content = f"{reasoning_prompt}\n\n{str(existing_content)}"
-
- messages[i] = cast(AllMessageValues, {
- **msg,
- "content": new_content
- })
+
+ messages[i] = cast(AllMessageValues, {**msg, "content": new_content})
break
else:
# Add new system message with reasoning instructions
- reasoning_message: AllMessageValues = cast(AllMessageValues, {
- "role": "system",
- "content": self._get_mistral_reasoning_system_prompt()
- })
+ reasoning_message: AllMessageValues = cast(
+ AllMessageValues, {"role": "system", "content": self._get_mistral_reasoning_system_prompt()}
+ )
messages = [reasoning_message] + messages
-
+
# Remove the internal flag
optional_params.pop("_add_reasoning_prompt", None)
return messages
@@ -307,7 +295,7 @@ class MistralConfig(OpenAIGPTConfig):
Otherwise, we drop `name`
"""
_name = message.get("name") # type: ignore
-
+
if _name is not None:
# Remove name if not a tool message
if message["role"] != "tool":
@@ -336,6 +324,26 @@ class MistralConfig(OpenAIGPTConfig):
message["tool_calls"] = mistral_tool_calls # type: ignore
return message
+ @staticmethod
+ def _handle_empty_content_response(response_data: dict) -> dict:
+ """
+ Handle Mistral-specific behavior where empty string content should be converted to None.
+
+ Mistral API sometimes returns empty string content ('') instead of null,
+ which can cause issues with downstream processing.
+
+ Args:
+ response_data: The raw response data from Mistral API
+
+ Returns:
+ dict: The response data with empty string content converted to None
+ """
+ if response_data.get("choices") and len(response_data["choices"]) > 0:
+ for choice in response_data["choices"]:
+ if choice.get("message") and choice["message"].get("content") == "":
+ choice["message"]["content"] = None
+ return response_data
+
def transform_request(
self,
model: str,
@@ -354,7 +362,7 @@ class MistralConfig(OpenAIGPTConfig):
# Add reasoning system prompt if needed (for magistral models)
if "magistral" in model.lower() and optional_params.get("_add_reasoning_prompt", False):
messages = self._add_reasoning_system_prompt_if_needed(messages, optional_params)
-
+
# Call parent transform_request which handles _transform_messages
return super().transform_request(
model=model,
@@ -363,3 +371,40 @@ class MistralConfig(OpenAIGPTConfig):
litellm_params=litellm_params,
headers=headers,
)
+
+ def transform_response(
+ self,
+ model: str,
+ raw_response: httpx.Response,
+ model_response: ModelResponse,
+ logging_obj: LiteLLMLoggingObj,
+ request_data: dict,
+ messages: List[AllMessageValues],
+ optional_params: dict,
+ litellm_params: dict,
+ encoding: Any,
+ api_key: Optional[str] = None,
+ json_mode: Optional[bool] = None,
+ ) -> ModelResponse:
+ """
+ Transform the raw response from Mistral API.
+ Handles Mistral-specific behavior like converting empty string content to None.
+ """
+ logging_obj.post_call(original_response=raw_response.text)
+ logging_obj.model_call_details["response_headers"] = raw_response.headers
+
+ # Handle Mistral-specific empty string content conversion to None
+ response_data = raw_response.json()
+ response_data = self._handle_empty_content_response(response_data)
+
+ final_response_obj = cast(
+ ModelResponse,
+ convert_to_model_response_object(
+ response_object=response_data,
+ model_response_object=model_response,
+ hidden_params={"headers": raw_response.headers},
+ _response_headers=dict(raw_response.headers),
+ ),
+ )
+
+ return final_response_obj
diff --git a/litellm/llms/mistral/embedding.py b/litellm/llms/mistral/embedding.py
index fc454038f1c..0aae35ad7f7 100644
--- a/litellm/llms/mistral/embedding.py
+++ b/litellm/llms/mistral/embedding.py
@@ -1,5 +1,4 @@
"""
Calls handled in openai/
-
as mistral is an openai-compatible endpoint.
-"""
+"""
\ No newline at end of file
diff --git a/litellm/llms/ollama/chat/transformation.py b/litellm/llms/ollama/chat/transformation.py
index dd0b42dd6c8..d4ce4052a7e 100644
--- a/litellm/llms/ollama/chat/transformation.py
+++ b/litellm/llms/ollama/chat/transformation.py
@@ -296,6 +296,12 @@ class OllamaChatConfig(BaseConfig):
cast(dict, m)["tool_calls"] = new_tools
new_messages.append(m)
+ # Load Config
+ config = self.get_config()
+ for k, v in config.items():
+ if k not in optional_params:
+ optional_params[k] = v
+
data = {
"model": model,
"messages": new_messages,
diff --git a/litellm/llms/openai/common_utils.py b/litellm/llms/openai/common_utils.py
index 8661cf43e25..aa670df0531 100644
--- a/litellm/llms/openai/common_utils.py
+++ b/litellm/llms/openai/common_utils.py
@@ -4,6 +4,7 @@ Common helpers / utils across al OpenAI endpoints
import hashlib
import json
+import ssl
from typing import Any, Dict, List, Literal, Optional, Union
import httpx
@@ -15,6 +16,7 @@ from litellm.llms.base_llm.chat.transformation import BaseLLMException
from litellm.llms.custom_httpx.http_handler import (
_DEFAULT_TTL_FOR_HTTPX_CLIENTS,
AsyncHTTPHandler,
+ get_ssl_configuration,
)
@@ -196,17 +198,29 @@ class BaseOpenAILLM:
if litellm.aclient_session is not None:
return litellm.aclient_session
+ # Get unified SSL configuration
+ ssl_config = get_ssl_configuration()
+
return httpx.AsyncClient(
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=100),
- verify=litellm.ssl_verify,
- transport=AsyncHTTPHandler._create_async_transport(),
+ verify=ssl_config,
+ transport=AsyncHTTPHandler._create_async_transport(
+ ssl_context=ssl_config if isinstance(ssl_config, ssl.SSLContext) else None,
+ ssl_verify=ssl_config if isinstance(ssl_config, bool) else None,
+ ),
+ follow_redirects=True,
)
@staticmethod
def _get_sync_http_client() -> Optional[httpx.Client]:
if litellm.client_session is not None:
return litellm.client_session
+
+ # Get unified SSL configuration
+ ssl_config = get_ssl_configuration()
+
return httpx.Client(
limits=httpx.Limits(max_connections=1000, max_keepalive_connections=100),
- verify=litellm.ssl_verify,
+ verify=ssl_config,
+ follow_redirects=True,
)
diff --git a/litellm/llms/openai/responses/transformation.py b/litellm/llms/openai/responses/transformation.py
index cf742bc52cb..527ae4a9d49 100644
--- a/litellm/llms/openai/responses/transformation.py
+++ b/litellm/llms/openai/responses/transformation.py
@@ -11,6 +11,7 @@ from litellm.types.responses.main import *
from litellm.types.router import GenericLiteLLMParams
from ..common_utils import OpenAIError
+from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import _safe_convert_created_field
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
@@ -85,6 +86,7 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
"""No transform applied since outputs are in OpenAI spec already"""
try:
raw_response_json = raw_response.json()
+ raw_response_json["created_at"] = _safe_convert_created_field(raw_response_json["created_at"])
except Exception:
raise OpenAIError(
message=raw_response.text, status_code=raw_response.status_code
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 20cf076d415..2c72046e2ab 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
@@ -834,16 +834,15 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
@staticmethod
def _transform_parts(
parts: List[HttpxPartType],
+ cumulative_tool_call_idx: int,
is_function_call: Optional[bool],
) -> Tuple[
Optional[ChatCompletionToolCallFunctionChunk],
Optional[List[ChatCompletionToolCallChunk]],
+ int,
]:
function: Optional[ChatCompletionToolCallFunctionChunk] = None
_tools: List[ChatCompletionToolCallChunk] = []
- # in a single chunk, each tool call appears as a separate part
- # they need to be separate indexes as they are separate tool calls
- funcCallIndex = 0
for part in parts:
if "functionCall" in part:
_function_chunk = ChatCompletionToolCallFunctionChunk(
@@ -857,15 +856,15 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
id=f"call_{str(uuid.uuid4())}",
type="function",
function=_function_chunk,
- index=funcCallIndex,
+ index=cumulative_tool_call_idx,
)
_tools.append(_tool_response_chunk)
- funcCallIndex += 1
+ cumulative_tool_call_idx += 1
if len(_tools) == 0:
tools: Optional[List[ChatCompletionToolCallChunk]] = None
else:
tools = _tools
- return function, tools
+ return function, tools, cumulative_tool_call_idx
@staticmethod
def _transform_logprobs(
@@ -1077,8 +1076,10 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
elif (
finish_reason and finish_reason in mapped_finish_reason.keys()
): # vertex ai
+
return mapped_finish_reason[finish_reason]
else:
+
return "stop"
@staticmethod
@@ -1126,6 +1127,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
chat_completion_logprobs: Optional[ChoiceLogprobs] = None
tools: Optional[List[ChatCompletionToolCallChunk]] = []
functions: Optional[ChatCompletionToolCallFunctionChunk] = None
+ cumulative_tool_call_index: int = 0
for idx, candidate in enumerate(_candidates):
if "content" not in candidate:
@@ -1172,8 +1174,9 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
if reasoning_content is not None:
chat_completion_message["reasoning_content"] = reasoning_content
- functions, tools = VertexGeminiConfig._transform_parts(
+ functions, tools, cumulative_tool_call_index = VertexGeminiConfig._transform_parts(
parts=candidate["content"]["parts"],
+ cumulative_tool_call_idx=cumulative_tool_call_index,
is_function_call=is_function_call(standard_optional_params),
)
@@ -1261,7 +1264,6 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
status_code=422,
headers=raw_response.headers,
)
-
return self._transform_google_generate_content_to_openai_model_response(
completion_response=completion_response,
@@ -1270,7 +1272,6 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
logging_obj=logging_obj,
raw_response=raw_response,
)
-
def _transform_google_generate_content_to_openai_model_response(
self,
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 36c1704439c..7303ab0786c 100644
--- a/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py
+++ b/litellm/llms/vertex_ai/vertex_ai_partner_models/main.py
@@ -32,6 +32,26 @@ class VertexAIPartnerModels(VertexBase):
def __init__(self) -> None:
pass
+ @staticmethod
+ def is_vertex_partner_model(model: str):
+ """
+ Check if the model string is a Vertex AI Partner Model
+ Only use this once you have confirmed that custom_llm_provider is vertex_ai
+
+ Returns:
+ bool: True if the model string is a Vertex AI Partner Model, False otherwise
+ """
+ if (
+ model.startswith("meta/")
+ or model.startswith("deepseek-ai")
+ or model.startswith("mistral")
+ or model.startswith("codestral")
+ or model.startswith("jamba")
+ or model.startswith("claude")
+ ):
+ return True
+ return False
+
def completion(
self,
model: str,
@@ -95,7 +115,7 @@ class VertexAIPartnerModels(VertexBase):
optional_params["stream"] = stream
- if "llama" in model:
+ if "llama" in model or "deepseek-ai" in model:
partner = VertexPartnerProvider.llama
elif "mistral" in model or "codestral" in model:
partner = VertexPartnerProvider.mistralai
diff --git a/litellm/llms/vertex_ai/vertex_llm_base.py b/litellm/llms/vertex_ai/vertex_llm_base.py
index f45549368a3..22f119dab2f 100644
--- a/litellm/llms/vertex_ai/vertex_llm_base.py
+++ b/litellm/llms/vertex_ai/vertex_llm_base.py
@@ -83,7 +83,11 @@ class VertexBase:
if "type" in json_obj and json_obj["type"] == "external_account":
# If environment_id key contains "aws" value it corresponds to an AWS config file
credential_source = json_obj.get("credential_source", {})
- environment_id = credential_source.get("environment_id", "") if isinstance(credential_source, dict) else ""
+ environment_id = (
+ credential_source.get("environment_id", "")
+ if isinstance(credential_source, dict)
+ else ""
+ )
if isinstance(environment_id, str) and "aws" in environment_id:
creds = self._credentials_from_identity_pool_with_aws(json_obj)
else:
@@ -130,7 +134,7 @@ class VertexBase:
from google.auth import identity_pool
return identity_pool.Credentials.from_info(json_obj)
-
+
def _credentials_from_identity_pool_with_aws(self, json_obj):
from google.auth import aws
@@ -183,7 +187,7 @@ class VertexBase:
api_base = api_base or f"https://{vertex_location}-aiplatform.googleapis.com"
if partner == VertexPartnerProvider.llama:
- return f"{api_base}/v1beta1/projects/{vertex_project}/locations/{vertex_location}/endpoints/openapi/chat/completions"
+ return f"{api_base}/v1/projects/{vertex_project}/locations/{vertex_location}/endpoints/openapi/chat/completions"
elif partner == VertexPartnerProvider.mistralai:
if stream:
return f"{api_base}/v1/projects/{vertex_project}/locations/{vertex_location}/publishers/mistralai/models/{model}:streamRawPredict"
@@ -490,7 +494,7 @@ class VertexBase:
headers.update(extra_headers)
return headers
-
+
@staticmethod
def get_vertex_ai_project(litellm_params: dict) -> Optional[str]:
return (
@@ -499,7 +503,7 @@ class VertexBase:
or litellm.vertex_project
or get_secret_str("VERTEXAI_PROJECT")
)
-
+
@staticmethod
def get_vertex_ai_credentials(litellm_params: dict) -> Optional[str]:
return (
@@ -507,7 +511,7 @@ class VertexBase:
or litellm_params.pop("vertex_ai_credentials", None)
or get_secret_str("VERTEXAI_CREDENTIALS")
)
-
+
@staticmethod
def get_vertex_ai_location(litellm_params: dict) -> Optional[str]:
return (
diff --git a/litellm/llms/watsonx/completion/transformation.py b/litellm/llms/watsonx/completion/transformation.py
index d45704840fe..a0b9735a990 100644
--- a/litellm/llms/watsonx/completion/transformation.py
+++ b/litellm/llms/watsonx/completion/transformation.py
@@ -300,9 +300,14 @@ class IBMWatsonXAIConfig(IBMWatsonXMixin, BaseConfig):
json_resp["results"][0]["stop_reason"]
)
if json_resp.get("created_at"):
- model_response.created = int(
- datetime.fromisoformat(json_resp["created_at"]).timestamp()
- )
+ try:
+ created_datetime = datetime.fromisoformat(json_resp["created_at"])
+ except ValueError:
+ # datetime.fromisoformat cannot handle 'Z' in Python 3.10
+ created_datetime = datetime.fromisoformat(
+ f'{json_resp["created_at"].rstrip("Z")}+00:00'
+ )
+ model_response.created = int(created_datetime.timestamp())
else:
model_response.created = int(time.time())
usage = Usage(
diff --git a/litellm/main.py b/litellm/main.py
index c75f5bfe675..fec5f2e4703 100644
--- a/litellm/main.py
+++ b/litellm/main.py
@@ -31,6 +31,7 @@ from typing import (
Literal,
Mapping,
Optional,
+ Tuple,
Type,
Union,
cast,
@@ -105,6 +106,7 @@ from litellm.utils import (
mock_completion_streaming_obj,
pre_process_non_default_params,
read_config_args,
+ should_run_mock_completion,
supports_httpx_timeout,
token_counter,
validate_and_fix_openai_messages,
@@ -436,6 +438,7 @@ async def acompletion(
prompt_variables=kwargs.get("prompt_variables", None),
tools=tools,
prompt_label=kwargs.get("prompt_label", None),
+ prompt_version=kwargs.get("prompt_version", None),
)
#########################################################
# if the chat completion logging hook removed all tools,
@@ -450,6 +453,7 @@ async def acompletion(
#########################################################
#########################################################
+
# Adjusted to use explicit arguments instead of *args and **kwargs
completion_kwargs = {
"model": model,
@@ -506,6 +510,15 @@ async def acompletion(
)
return response
+ ### APPLY MOCK DELAY ###
+
+ mock_delay = kwargs.get("mock_delay")
+ mock_response = kwargs.get("mock_response")
+ mock_tool_calls = kwargs.get("mock_tool_calls")
+ mock_timeout = kwargs.get("mock_timeout")
+ if mock_delay and should_run_mock_completion(mock_response=mock_response, mock_tool_calls=mock_tool_calls, mock_timeout=mock_timeout):
+ await asyncio.sleep(mock_delay)
+
try:
# Use a partial function to pass your keyword arguments
func = partial(completion, **completion_kwargs, **kwargs)
@@ -672,6 +685,7 @@ async def _sleep_for_timeout_async(timeout: Union[float, str, httpx.Timeout]):
await asyncio.sleep(timeout.connect)
+
def mock_completion(
model: str,
messages: List,
@@ -709,6 +723,7 @@ def mock_completion(
- If 'stream' is True, it returns a response that mimics the behavior of a streaming completion.
"""
try:
+ is_acompletion = kwargs.get("acompletion") or False
if mock_response is None:
mock_response = "This is a mock request"
@@ -740,7 +755,7 @@ def mock_completion(
status_code=529,
)
time_delay = kwargs.get("mock_delay", None)
- if time_delay is not None:
+ if time_delay is not None and not is_acompletion:
time.sleep(time_delay)
if isinstance(mock_response, dict):
@@ -822,6 +837,34 @@ def mock_completion(
raise Exception("Mock completion response failed - {}".format(e))
+def responses_api_bridge_check(
+ model: str,
+ custom_llm_provider: str,
+) -> Tuple[dict, str]:
+ model_info: Dict[str, Any] = {}
+ try:
+ model_info = cast(
+ dict,
+ _get_model_info_helper(
+ model=model, custom_llm_provider=custom_llm_provider
+ ),
+ )
+ if model_info.get("mode") is None and model.startswith("responses/"):
+ model = model.replace("responses/", "")
+ mode = "responses"
+ model_info["mode"] = mode
+ except Exception as e:
+ verbose_logger.debug("Error getting model info: {}".format(e))
+
+ if model.startswith(
+ "responses/"
+ ): # handle azure models - `azure/responses/`
+ model = model.replace("responses/", "")
+ mode = "responses"
+ model_info["mode"] = mode
+ return model_info, model
+
+
@tracer.wrap()
@client
def completion( # type: ignore # noqa: PLR0915
@@ -1020,6 +1063,7 @@ def completion( # type: ignore # noqa: PLR0915
prompt_id=prompt_id,
prompt_variables=prompt_variables,
prompt_label=kwargs.get("prompt_label", None),
+ prompt_version=kwargs.get("prompt_version", None),
)
try:
@@ -1290,19 +1334,9 @@ def completion( # type: ignore # noqa: PLR0915
)
## RESPONSES API BRIDGE LOGIC ## - check if model has 'mode: responses' in litellm.model_cost map
- try:
- model_info = _get_model_info_helper(
- model=model, custom_llm_provider=custom_llm_provider
- )
- except Exception as e:
- verbose_logger.debug("Error getting model info: {}".format(e))
- model_info = {}
- if model.startswith(
- "responses/"
- ): # handle azure models - `azure/responses/`
- model = model.split("/")[1]
- mode = "responses"
- model_info["mode"] = mode
+ model_info, model = responses_api_bridge_check(
+ model=model, custom_llm_provider=custom_llm_provider
+ )
if model_info.get("mode") == "responses":
from litellm.completion_extras import responses_api_bridge
@@ -1822,7 +1856,6 @@ def completion( # type: ignore # noqa: PLR0915
or custom_llm_provider == "sambanova"
or custom_llm_provider == "volcengine"
or custom_llm_provider == "anyscale"
- or custom_llm_provider == "mistral"
or custom_llm_provider == "openai"
or custom_llm_provider == "together_ai"
or custom_llm_provider == "nebius"
@@ -1911,6 +1944,33 @@ def completion( # type: ignore # noqa: PLR0915
additional_args={"headers": headers},
)
+ elif custom_llm_provider == "mistral":
+ api_key = api_key or litellm.api_key or get_secret("MISTRAL_API_KEY")
+ api_base = (
+ api_base
+ or litellm.api_base
+ or get_secret("MISTRAL_API_BASE")
+ or "https://api.mistral.ai/v1"
+ )
+
+ response = base_llm_http_handler.completion(
+ model=model,
+ messages=messages,
+ api_base=api_base,
+ custom_llm_provider=custom_llm_provider,
+ model_response=model_response,
+ encoding=encoding,
+ logging_obj=logging,
+ optional_params=optional_params,
+ timeout=timeout,
+ litellm_params=litellm_params,
+ acompletion=acompletion,
+ stream=stream,
+ api_key=api_key,
+ headers=headers,
+ client=client,
+ provider_config=provider_config,
+ )
elif (
"replicate" in model
or custom_llm_provider == "replicate"
@@ -2557,13 +2617,7 @@ def completion( # type: ignore # noqa: PLR0915
api_base = api_base or litellm.api_base or get_secret("VERTEXAI_API_BASE")
new_params = deepcopy(optional_params)
- if (
- model.startswith("meta/")
- or model.startswith("mistral")
- or model.startswith("codestral")
- or model.startswith("jamba")
- or model.startswith("claude")
- ):
+ if vertex_partner_models_chat_completion.is_vertex_partner_model(model):
model_response = vertex_partner_models_chat_completion.completion(
model=model,
messages=messages,
@@ -2816,9 +2870,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":
@@ -3241,6 +3295,7 @@ def completion( # type: ignore # noqa: PLR0915
prompt = " ".join([message["content"] for message in messages]) # type: ignore
resp = litellm.module_level_client.post(
url,
+ headers=headers,
json={
"model": model,
"params": {
@@ -3250,6 +3305,7 @@ def completion( # type: ignore # noqa: PLR0915
"top_p": top_p,
"top_k": kwargs.get("top_k"),
},
+ **kwargs.get("extra_body", {}),
},
)
response_json = resp.json()
@@ -3462,6 +3518,82 @@ async def aembedding(*args, **kwargs) -> EmbeddingResponse:
)
+# Overload for when aembedding=True (returns coroutine)
+@overload
+def embedding(
+ model,
+ input=[],
+ # Optional params
+ dimensions: Optional[int] = None,
+ encoding_format: Optional[str] = None,
+ timeout=600, # default to 10 minutes
+ # set api_base, api_version, api_key
+ api_base: Optional[str] = None,
+ api_version: Optional[str] = None,
+ api_key: Optional[str] = None,
+ api_type: Optional[str] = None,
+ caching: bool = False,
+ user: Optional[str] = None,
+ custom_llm_provider=None,
+ litellm_call_id=None,
+ logger_fn=None,
+ *,
+ aembedding: Literal[True],
+ **kwargs,
+) -> Coroutine[Any, Any, EmbeddingResponse]:
+ ...
+
+
+# Overload for when aembedding=False or not specified (returns EmbeddingResponse)
+@overload
+def embedding(
+ model,
+ input=[],
+ # Optional params
+ dimensions: Optional[int] = None,
+ encoding_format: Optional[str] = None,
+ timeout=600, # default to 10 minutes
+ # set api_base, api_version, api_key
+ api_base: Optional[str] = None,
+ api_version: Optional[str] = None,
+ api_key: Optional[str] = None,
+ api_type: Optional[str] = None,
+ caching: bool = False,
+ user: Optional[str] = None,
+ custom_llm_provider=None,
+ litellm_call_id=None,
+ logger_fn=None,
+ *,
+ aembedding: Literal[False] = False,
+ **kwargs,
+) -> EmbeddingResponse:
+ ...
+
+
+# Overload for when aembedding is not specified at all (returns EmbeddingResponse)
+@overload
+def embedding(
+ model,
+ input=[],
+ # Optional params
+ dimensions: Optional[int] = None,
+ encoding_format: Optional[str] = None,
+ timeout=600, # default to 10 minutes
+ # set api_base, api_version, api_key
+ api_base: Optional[str] = None,
+ api_version: Optional[str] = None,
+ api_key: Optional[str] = None,
+ api_type: Optional[str] = None,
+ caching: bool = False,
+ user: Optional[str] = None,
+ custom_llm_provider=None,
+ litellm_call_id=None,
+ logger_fn=None,
+ **kwargs,
+) -> EmbeddingResponse:
+ ...
+
+
@client
def embedding( # noqa: PLR0915
model,
@@ -4615,9 +4747,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
@@ -4957,7 +5089,10 @@ def transcription(
provider_config=provider_config,
litellm_params=litellm_params_dict,
)
- elif custom_llm_provider in [LlmProviders.DEEPGRAM.value, LlmProviders.ELEVENLABS.value]:
+ elif custom_llm_provider in [
+ LlmProviders.DEEPGRAM.value,
+ LlmProviders.ELEVENLABS.value,
+ ]:
response = base_llm_http_handler.audio_transcriptions(
model=model,
audio_file=file,
@@ -5605,9 +5740,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)
reasoning_chunks = [
chunk
@@ -5618,9 +5753,9 @@ def stream_chunk_builder( # noqa: PLR0915
]
if len(reasoning_chunks) > 0:
- response["choices"][0]["message"]["reasoning_content"] = (
- processor.get_combined_reasoning_content(reasoning_chunks)
- )
+ response["choices"][0]["message"][
+ "reasoning_content"
+ ] = processor.get_combined_reasoning_content(reasoning_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 b69d9984f62..bc19668e6a1 100644
--- a/litellm/model_prices_and_context_window_backup.json
+++ b/litellm/model_prices_and_context_window_backup.json
@@ -314,6 +314,24 @@
"supports_response_schema": true,
"supports_system_messages": true
},
+ "watsonx/mistralai/mistral-large": {
+ "max_tokens": 131072,
+ "max_input_tokens": 131072,
+ "max_output_tokens": 16384,
+ "input_cost_per_token": 0.000003,
+ "output_cost_per_token": 0.00001,
+ "litellm_provider": "watsonx",
+ "mode": "chat",
+ "supports_function_calling": true,
+ "supports_tool_choice": true,
+ "supports_parallel_function_calling": false,
+ "supports_vision": false,
+ "supports_audio_input": false,
+ "supports_audio_output": false,
+ "supports_prompt_caching": true,
+ "supports_response_schema": true,
+ "supports_system_messages": true
+ },
"gpt-4o-search-preview-2025-03-11": {
"max_tokens": 16384,
"max_input_tokens": 128000,
@@ -750,10 +768,10 @@
"max_tokens": 100000,
"max_input_tokens": 200000,
"max_output_tokens": 100000,
- "input_cost_per_token": 10e-06,
- "output_cost_per_token": 40e-06,
+ "input_cost_per_token": 1e-05,
+ "output_cost_per_token": 4e-05,
"input_cost_per_token_batches": 5e-06,
- "output_cost_per_token_batches": 20e-06,
+ "output_cost_per_token_batches": 2e-05,
"cache_read_input_token_cost": 2.5e-06,
"litellm_provider": "openai",
"mode": "responses",
@@ -783,10 +801,10 @@
"max_tokens": 100000,
"max_input_tokens": 200000,
"max_output_tokens": 100000,
- "input_cost_per_token": 10e-06,
- "output_cost_per_token": 40e-06,
+ "input_cost_per_token": 1e-05,
+ "output_cost_per_token": 4e-05,
"input_cost_per_token_batches": 5e-06,
- "output_cost_per_token_batches": 20e-06,
+ "output_cost_per_token_batches": 2e-05,
"cache_read_input_token_cost": 2.5e-06,
"litellm_provider": "openai",
"mode": "responses",
@@ -994,7 +1012,7 @@
"output_cost_per_token": 8e-06,
"input_cost_per_token_batches": 1e-06,
"output_cost_per_token_batches": 4e-06,
- "cache_read_input_token_cost": 0.5e-06,
+ "cache_read_input_token_cost": 5e-07,
"litellm_provider": "openai",
"mode": "responses",
"supported_endpoints": [
@@ -1027,7 +1045,7 @@
"output_cost_per_token": 8e-06,
"input_cost_per_token_batches": 1e-06,
"output_cost_per_token_batches": 4e-06,
- "cache_read_input_token_cost": 0.5e-06,
+ "cache_read_input_token_cost": 5e-07,
"litellm_provider": "openai",
"mode": "responses",
"supported_endpoints": [
@@ -4068,6 +4086,17 @@
"source": "https://azure.microsoft.com/en-us/pricing/details/phi-3/",
"supports_tool_choice": true
},
+ "azure_ai/cohere-rerank-v3.5": {
+ "max_tokens": 4096,
+ "max_input_tokens": 4096,
+ "max_output_tokens": 4096,
+ "max_query_tokens": 2048,
+ "input_cost_per_token": 0.0,
+ "input_cost_per_query": 0.002,
+ "output_cost_per_token": 0.0,
+ "litellm_provider": "azure_ai",
+ "mode": "rerank"
+ },
"azure_ai/cohere-rerank-v3-multilingual": {
"max_tokens": 4096,
"max_input_tokens": 4096,
@@ -10933,8 +10962,7 @@
"output_cost_per_token": 2.4e-05,
"litellm_provider": "bedrock",
"mode": "chat",
- "supports_function_calling": true,
- "supports_tool_choice": true
+ "supports_function_calling": true
},
"mistral.mistral-large-2407-v1:0": {
"max_tokens": 8191,
@@ -10955,8 +10983,7 @@
"output_cost_per_token": 3e-06,
"litellm_provider": "bedrock",
"mode": "chat",
- "supports_function_calling": true,
- "supports_tool_choice": true
+ "supports_function_calling": true
},
"bedrock/us-west-2/mistral.mixtral-8x7b-instruct-v0:1": {
"max_tokens": 8191,
@@ -11026,8 +11053,7 @@
"output_cost_per_token": 2.4e-05,
"litellm_provider": "bedrock",
"mode": "chat",
- "supports_function_calling": true,
- "supports_tool_choice": true
+ "supports_function_calling": true
},
"bedrock/us-west-2/mistral.mistral-large-2402-v1:0": {
"max_tokens": 8191,
@@ -11037,8 +11063,7 @@
"output_cost_per_token": 2.4e-05,
"litellm_provider": "bedrock",
"mode": "chat",
- "supports_function_calling": true,
- "supports_tool_choice": true
+ "supports_function_calling": true
},
"bedrock/eu-west-3/mistral.mistral-large-2402-v1:0": {
"max_tokens": 8191,
@@ -11048,8 +11073,7 @@
"output_cost_per_token": 3.12e-05,
"litellm_provider": "bedrock",
"mode": "chat",
- "supports_function_calling": true,
- "supports_tool_choice": true
+ "supports_function_calling": true
},
"amazon.nova-micro-v1:0": {
"max_tokens": 10000,
@@ -15744,7 +15768,7 @@
},
"elevenlabs/scribe_v1": {
"mode": "audio_transcription",
- "input_cost_per_second": 0.0000611,
+ "input_cost_per_second": 6.11e-05,
"output_cost_per_second": 0.0,
"litellm_provider": "elevenlabs",
"supported_endpoints": [
@@ -15759,7 +15783,7 @@
},
"elevenlabs/scribe_v1_experimental": {
"mode": "audio_transcription",
- "input_cost_per_second": 0.0000611,
+ "input_cost_per_second": 6.11e-05,
"output_cost_per_second": 0.0,
"litellm_provider": "elevenlabs",
"supported_endpoints": [
@@ -15772,4 +15796,4 @@
"notes": "ElevenLabs Scribe v1 experimental - enhanced version of the main Scribe model"
}
}
-}
+}
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/mcp_server/auth/litellm_auth_handler.py b/litellm/proxy/_experimental/mcp_server/auth/litellm_auth_handler.py
index b04fc3a0a49..3c62b9e4eac 100644
--- a/litellm/proxy/_experimental/mcp_server/auth/litellm_auth_handler.py
+++ b/litellm/proxy/_experimental/mcp_server/auth/litellm_auth_handler.py
@@ -1,15 +1,21 @@
-from typing import Optional
+from typing import Optional, List
from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser
from litellm.proxy._types import UserAPIKeyAuth
-class LiteLLMAuthenticatedUser(AuthenticatedUser):
+class MCPAuthenticatedUser(AuthenticatedUser):
"""
- Wrapper class to make UserAPIKeyAuth compatible with MCP's AuthenticatedUser
+ Wrapper class to make LiteLLM's authentication and configuration compatible with MCP's AuthenticatedUser.
+
+ This class handles:
+ 1. User API key authentication information
+ 2. MCP authentication header
+ 3. MCP server configuration
"""
- def __init__(self, user_api_key_auth: UserAPIKeyAuth, mcp_auth_header: Optional[str] = None):
+ def __init__(self, user_api_key_auth: UserAPIKeyAuth, mcp_auth_header: Optional[str] = None, mcp_servers: Optional[List[str]] = None):
self.user_api_key_auth = user_api_key_auth
self.mcp_auth_header = mcp_auth_header
+ self.mcp_servers = mcp_servers
diff --git a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py
index 177fbafa5e3..fba7928e7ac 100644
--- a/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py
+++ b/litellm/proxy/_experimental/mcp_server/auth/user_api_key_auth_mcp.py
@@ -9,11 +9,14 @@ from litellm.proxy._types import LiteLLM_TeamTable, SpecialHeaders, UserAPIKeyAu
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
-class UserAPIKeyAuthMCP:
+class MCPRequestHandler:
"""
- Class to handle Authentication for MCP requests
+ Class to handle MCP request processing, including:
+ 1. Authentication via LiteLLM API keys
+ 2. MCP server configuration and routing
+ 3. Header extraction and validation
- Utilizes the main `user_api_key_auth` function to validate the request
+ Utilizes the main `user_api_key_auth` function to validate authentication
"""
LITELLM_API_KEY_HEADER_NAME_PRIMARY = SpecialHeaders.custom_litellm_api_key.value
@@ -22,10 +25,16 @@ class UserAPIKeyAuthMCP:
# This is the header to use if you want LiteLLM to use this header for authenticating to the MCP server
LITELLM_MCP_AUTH_HEADER_NAME = SpecialHeaders.mcp_auth.value
+ LITELLM_MCP_SERVERS_HEADER_NAME = SpecialHeaders.mcp_servers.value
+
@staticmethod
- async def user_api_key_auth_mcp(scope: Scope) -> Tuple[UserAPIKeyAuth, Optional[str]]:
+ async def process_mcp_request(scope: Scope) -> Tuple[UserAPIKeyAuth, Optional[str], Optional[List[str]]]:
"""
- Validate and extract headers from the ASGI scope for MCP requests.
+ Process and validate MCP request headers from the ASGI scope.
+ This includes:
+ 1. Extracting and validating authentication headers
+ 2. Processing MCP server configuration
+ 3. Handling MCP-specific headers
Args:
scope: ASGI scope containing request information
@@ -33,15 +42,31 @@ class UserAPIKeyAuthMCP:
Returns:
UserAPIKeyAuth containing validated authentication information
mcp_auth_header: Optional[str] MCP auth header to be passed to the MCP server
+ mcp_servers: Optional[List[str]] List of MCP servers to use
Raises:
HTTPException: If headers are invalid or missing required headers
"""
- headers = UserAPIKeyAuthMCP._safe_get_headers_from_scope(scope)
+ headers = MCPRequestHandler._safe_get_headers_from_scope(scope)
litellm_api_key = (
- UserAPIKeyAuthMCP.get_litellm_api_key_from_headers(headers) or ""
+ MCPRequestHandler.get_litellm_api_key_from_headers(headers) or ""
)
- mcp_auth_header = headers.get(UserAPIKeyAuthMCP.LITELLM_MCP_AUTH_HEADER_NAME)
+ mcp_auth_header = headers.get(MCPRequestHandler.LITELLM_MCP_AUTH_HEADER_NAME)
+ mcp_servers_header = headers.get(MCPRequestHandler.LITELLM_MCP_SERVERS_HEADER_NAME)
+ verbose_logger.debug(f"Raw MCP servers header: {mcp_servers_header}")
+ mcp_servers = None
+ if mcp_servers_header is not None: # Changed from 'if mcp_servers_header:' to handle empty strings
+ try:
+ # Parse as comma-separated list
+ mcp_servers = [s.strip() for s in mcp_servers_header.split(",") if s.strip()]
+ verbose_logger.debug(f"Parsed MCP servers: {mcp_servers}")
+ except Exception as e:
+ verbose_logger.debug(f"Error parsing mcp_servers header: {e}")
+ mcp_servers = None
+
+ # If we got an empty string or parsing resulted in no servers, return empty list
+ if mcp_servers_header == "" or (mcp_servers is not None and len(mcp_servers) == 0):
+ mcp_servers = []
# Create a proper Request object with mock body method to avoid ASGI receive channel issues
request = Request(scope=scope)
@@ -57,7 +82,7 @@ class UserAPIKeyAuthMCP:
api_key=litellm_api_key, request=request
)
- return validated_user_api_key_auth, mcp_auth_header
+ return validated_user_api_key_auth, mcp_auth_header, mcp_servers
@staticmethod
def get_litellm_api_key_from_headers(headers: Headers) -> Optional[str]:
@@ -71,12 +96,12 @@ class UserAPIKeyAuthMCP:
headers: Starlette Headers object that handles case insensitivity
"""
# Headers object handles case insensitivity automatically
- api_key = headers.get(UserAPIKeyAuthMCP.LITELLM_API_KEY_HEADER_NAME_PRIMARY)
+ api_key = headers.get(MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_PRIMARY)
if api_key:
return api_key
auth_header = headers.get(
- UserAPIKeyAuthMCP.LITELLM_API_KEY_HEADER_NAME_SECONDARY
+ MCPRequestHandler.LITELLM_API_KEY_HEADER_NAME_SECONDARY
)
if auth_header:
return auth_header
@@ -101,7 +126,7 @@ class UserAPIKeyAuthMCP:
for name, value in raw_headers
}
return Headers(headers_dict)
- except Exception as e:
+ except (UnicodeDecodeError, AttributeError, TypeError) as e:
verbose_logger.exception(f"Error getting headers from scope: {e}")
# Return empty Headers object with empty dict
return Headers({})
@@ -111,16 +136,16 @@ class UserAPIKeyAuthMCP:
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
) -> List[str]:
"""
- Apply least privilege
+ Get list of allowed MCP servers for the given user/key based on permissions
"""
from typing import List
allowed_mcp_servers: List[str] = []
allowed_mcp_servers_for_key = (
- await UserAPIKeyAuthMCP._get_allowed_mcp_servers_for_key(user_api_key_auth)
+ await MCPRequestHandler._get_allowed_mcp_servers_for_key(user_api_key_auth)
)
allowed_mcp_servers_for_team = (
- await UserAPIKeyAuthMCP._get_allowed_mcp_servers_for_team(user_api_key_auth)
+ await MCPRequestHandler._get_allowed_mcp_servers_for_team(user_api_key_auth)
)
#########################################################
@@ -196,4 +221,4 @@ class UserAPIKeyAuthMCP:
if object_permissions is None:
return []
- return object_permissions.mcp_servers or []
+ return object_permissions.mcp_servers or []
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py
index d32a1779145..6cafaeec3c4 100644
--- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py
+++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py
@@ -18,7 +18,7 @@ from mcp.types import Tool as MCPTool
from litellm._logging import verbose_logger
from litellm.experimental_mcp_client.client import MCPClient
from litellm.proxy._experimental.mcp_server.auth.user_api_key_auth_mcp import (
- UserAPIKeyAuthMCP,
+ MCPRequestHandler,
)
from litellm.proxy._types import (
LiteLLM_MCPServerTable,
@@ -30,6 +30,7 @@ from litellm.proxy._types import (
UserAPIKeyAuth,
)
from litellm.types.mcp_server.mcp_server_manager import MCPInfo, MCPServer
+from litellm.proxy._experimental.mcp_server.utils import add_server_prefix_to_tool_name, normalize_server_name, get_server_name_prefix_tool_mcp, is_tool_name_prefixed
class MCPServerManager:
@@ -143,7 +144,7 @@ class MCPServerManager:
"""
Get the allowed MCP Servers for the user
"""
- allowed_mcp_servers = await UserAPIKeyAuthMCP.get_allowed_mcp_servers(
+ allowed_mcp_servers = await MCPRequestHandler.get_allowed_mcp_servers(
user_api_key_auth
)
verbose_logger.debug(
@@ -216,56 +217,114 @@ class MCPServerManager:
async def _get_tools_from_server(self, server: MCPServer, mcp_auth_header: Optional[str] = None) -> List[MCPTool]:
"""
- Helper method to get tools from a single MCP server.
+ Helper method to get tools from a single MCP server with prefixed names.
Args:
server (MCPServer): The server to query tools from
+ mcp_auth_header: Optional auth header for MCP server
Returns:
- List[MCPTool]: List of tools available on the server
+ List[MCPTool]: List of tools available on the server with prefixed names
"""
verbose_logger.debug(f"Connecting to url: {server.url}")
verbose_logger.info("_get_tools_from_server...")
- client = self._create_mcp_client(
- server=server,
- mcp_auth_header=mcp_auth_header,
- )
- async with client:
- tools = await client.list_tools()
- verbose_logger.debug(f"Tools from {server.name}: {tools}")
+ client = None
+ try:
+ client = self._create_mcp_client(
+ server=server,
+ mcp_auth_header=mcp_auth_header,
+ )
- # Update tool to server mapping
- for tool in tools:
- self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
+ # Create a task for the client operations to ensure proper cancellation handling
+ async def _list_tools_task():
+ async with client:
+ tools = await client.list_tools()
+ verbose_logger.debug(f"Tools from {server.name}: {tools}")
+ return tools
+
+ try:
+ tools = await _list_tools_task()
+
+ # Create new tools with prefixed names
+ prefixed_tools = []
+ for tool in tools:
+ # Create prefixed tool name
+ prefixed_name = add_server_prefix_to_tool_name(tool.name, server.name)
+
+ # Create new tool with prefixed name
+ prefixed_tool = MCPTool(
+ name=prefixed_name,
+ description=tool.description,
+ inputSchema=tool.inputSchema
+ )
+ prefixed_tools.append(prefixed_tool)
+
+ # Update tool to server mapping with both original and prefixed names
+ self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
+ self.tool_name_to_mcp_server_name_mapping[prefixed_name] = server.name
+
+ return prefixed_tools
+ except asyncio.CancelledError:
+ verbose_logger.warning(f"Task cancelled while listing tools from {server.name}")
+ raise # Re-raise the cancellation
+ except Exception as e:
+ verbose_logger.exception(f"Error listing tools from {server.name}: {str(e)}")
+ raise
+ except Exception as e:
+ verbose_logger.exception(f"Failed to get tools from server {server.name}: {str(e)}")
+ return [] # Return empty list on failure
+ finally:
+ if client:
+ try:
+ await client.disconnect()
+ except Exception:
+ pass
- return tools
-
async def call_tool(
- self,
- name: str,
- arguments: Dict[str, Any],
- user_api_key_auth: Optional[UserAPIKeyAuth] = None,
- mcp_auth_header: Optional[str] = None,
+ self,
+ name: str,
+ arguments: Dict[str, Any],
+ user_api_key_auth: Optional[UserAPIKeyAuth] = None,
+ mcp_auth_header: Optional[str] = None,
) -> CallToolResult:
"""
- Call a tool with the given name and arguments
+ Call a tool with the given name and arguments (handles prefixed tool names)
+
+ Args:
+ name: Tool name (can be prefixed with server name)
+ arguments: Tool arguments
+ user_api_key_auth: User authentication
+ mcp_auth_header: MCP auth header
+
+ Returns:
+ CallToolResult from the MCP server
"""
+ # Remove prefix if present to get the original tool name
+ original_tool_name, server_name_from_prefix = get_server_name_prefix_tool_mcp(name)
+
+ # Get the MCP server
mcp_server = self._get_mcp_server_from_tool_name(name)
if mcp_server is None:
raise ValueError(f"Tool {name} not found")
+ # Validate that the server from prefix matches the actual server (if prefix was used)
+ if server_name_from_prefix and normalize_server_name(server_name_from_prefix) != normalize_server_name(mcp_server.name):
+ raise ValueError(
+ f"Tool {name} server prefix mismatch: expected {mcp_server.name}, got {server_name_from_prefix}")
+
client = self._create_mcp_client(
server=mcp_server,
mcp_auth_header=mcp_auth_header,
)
async with client:
+ # Use the original tool name (without prefix) for the actual call
call_tool_params = MCPCallToolRequestParams(
- name=name,
+ name=original_tool_name,
arguments=arguments,
)
return await client.call_tool(call_tool_params)
-
+
#########################################################
# End of Methods that call the upstream MCP servers
#########################################################
@@ -288,20 +347,41 @@ class MCPServerManager:
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
+ Note: This now handles prefixed tool names
"""
for server in self.get_registry().values():
tools = await self._get_tools_from_server(server)
for tool in tools:
+ # The tool.name here is already prefixed from _get_tools_from_server
+ # Extract original name for mapping
+ original_name, _ = get_server_name_prefix_tool_mcp(tool.name)
+ self.tool_name_to_mcp_server_name_mapping[original_name] = server.name
self.tool_name_to_mcp_server_name_mapping[tool.name] = server.name
def _get_mcp_server_from_tool_name(self, tool_name: str) -> Optional[MCPServer]:
"""
- Get the MCP Server from the tool name
+ Get the MCP Server from the tool name (handles both prefixed and non-prefixed names)
+
+ Args:
+ tool_name: Tool name (can be prefixed or non-prefixed)
+
+ Returns:
+ MCPServer if found, None otherwise
"""
+ # First try with the original tool name
if tool_name in self.tool_name_to_mcp_server_name_mapping:
+ server_name = self.tool_name_to_mcp_server_name_mapping[tool_name]
for server in self.get_registry().values():
- if server.name == self.tool_name_to_mcp_server_name_mapping[tool_name]:
+ if normalize_server_name(server.name) == normalize_server_name(server_name):
return server
+
+ # If not found and tool name is prefixed, try extracting server name from prefix
+ if is_tool_name_prefixed(tool_name):
+ _, server_name_from_prefix = get_server_name_prefix_tool_mcp(tool_name)
+ for server in self.get_registry().values():
+ if normalize_server_name(server.name) == normalize_server_name(server_name_from_prefix):
+ return server
+
return None
async def _add_mcp_servers_from_db_to_in_memory_registry(self):
diff --git a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py
index 9094be6f42e..b2ee6f26da5 100644
--- a/litellm/proxy/_experimental/mcp_server/rest_endpoints.py
+++ b/litellm/proxy/_experimental/mcp_server/rest_endpoints.py
@@ -1,5 +1,5 @@
import importlib
-from typing import List, Optional
+from typing import Optional
from fastapi import APIRouter, Depends, Query, Request
@@ -38,54 +38,98 @@ if MCP_AVAILABLE:
None, description="The server id to list tools for"
),
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
- ) -> List[ListMCPToolsRestAPIResponseObject]:
+ ) -> dict:
"""
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",
+ {
+ "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",
- }
- }
- ]
+ ],
+ "error": null,
+ "message": "Successfully retrieved tools"
+ }
"""
- list_tools_result: List[ListMCPToolsRestAPIResponseObject] = []
- for server in global_mcp_server_manager.get_registry().values():
- if server_id and server.server_id != server_id:
- continue
- try:
- tools = await global_mcp_server_manager._get_tools_from_server(
- 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,
- )
+ try:
+ list_tools_result = []
+ error_message = None
+
+ # If server_id is specified, only query that specific server
+ if server_id:
+ server = global_mcp_server_manager.get_mcp_server_by_id(server_id)
+ if server is None:
+ return {
+ "tools": [],
+ "error": "server_not_found",
+ "message": f"Server with id {server_id} not found"
+ }
+ try:
+ tools = await global_mcp_server_manager._get_tools_from_server(
+ server=server,
)
- except Exception as e:
- verbose_logger.exception(f"Error getting tools from {server.name}: {e}")
- continue
- return list_tools_result
+ 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}")
+ return {
+ "tools": [],
+ "error": "server_error",
+ "message": f"Failed to get tools from server {server.name}: {str(e)}"
+ }
+ else:
+ # Query all servers
+ errors = []
+ for server in global_mcp_server_manager.get_registry().values():
+ try:
+ tools = await global_mcp_server_manager._get_tools_from_server(
+ 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}")
+ errors.append(f"{server.name}: {str(e)}")
+ continue
+
+ if errors and not list_tools_result:
+ error_message = "Failed to get tools from servers: " + "; ".join(errors)
+
+ return {
+ "tools": list_tools_result,
+ "error": "partial_failure" if error_message else None,
+ "message": error_message if error_message else "Successfully retrieved tools"
+ }
+
+ except Exception as e:
+ verbose_logger.exception("Unexpected error in list_tool_rest_api: %s", str(e))
+ return {
+ "tools": [],
+ "error": "unexpected_error",
+ "message": f"An unexpected error occurred: {str(e)}"
+ }
@router.post("/tools/call", dependencies=[Depends(user_api_key_auth)])
async def call_tool_rest_api(
diff --git a/litellm/proxy/_experimental/mcp_server/server.py b/litellm/proxy/_experimental/mcp_server/server.py
index 83f922a223b..930d099f83c 100644
--- a/litellm/proxy/_experimental/mcp_server/server.py
+++ b/litellm/proxy/_experimental/mcp_server/server.py
@@ -14,17 +14,19 @@ 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._experimental.mcp_server.auth.user_api_key_auth_mcp import (
- UserAPIKeyAuthMCP,
+ MCPRequestHandler,
+)
+from litellm.proxy._experimental.mcp_server.utils import (
+ LITELLM_MCP_SERVER_DESCRIPTION,
+ LITELLM_MCP_SERVER_NAME,
+ LITELLM_MCP_SERVER_VERSION,
+ normalize_server_name,
)
from litellm.proxy._types import UserAPIKeyAuth
from litellm.types.mcp_server.mcp_server_manager import MCPInfo
from litellm.types.utils import StandardLoggingMCPToolCall
from litellm.utils import client
-LITELLM_MCP_SERVER_NAME = "litellm-mcp-server"
-LITELLM_MCP_SERVER_VERSION = "1.0.0"
-LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM"
-
# Check if MCP is available
# "mcp" requires python 3.10 or higher, but several litellm users use python 3.8
# We're making this conditional import to avoid breaking users who use python 3.8.
@@ -56,7 +58,7 @@ if MCP_AVAILABLE:
from mcp.types import Tool as MCPTool
from litellm.proxy._experimental.mcp_server.auth.litellm_auth_handler import (
- LiteLLMAuthenticatedUser,
+ MCPAuthenticatedUser,
)
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
@@ -65,6 +67,9 @@ if MCP_AVAILABLE:
from litellm.proxy._experimental.mcp_server.tool_registry import (
global_mcp_tool_registry,
)
+ from litellm.proxy._experimental.mcp_server.utils import (
+ get_server_name_prefix_tool_mcp,
+ )
######################################################
############ MCP Tools List REST API Response Object #
@@ -166,13 +171,15 @@ if MCP_AVAILABLE:
List all available tools
"""
# Get user authentication from context variable
- user_api_key_auth, mcp_auth_header = get_auth_context()
+ user_api_key_auth, mcp_auth_header, mcp_servers = get_auth_context()
verbose_logger.debug(
f"MCP list_tools - User API Key Auth from context: {user_api_key_auth}"
)
+ # Get mcp_servers from context variable
return await _list_mcp_tools(
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
+ mcp_servers=mcp_servers,
)
@server.call_tool()
@@ -193,7 +200,7 @@ if MCP_AVAILABLE:
HTTPException: If tool not found or arguments missing
"""
# Validate arguments
- user_api_key_auth, mcp_auth_header = get_auth_context()
+ user_api_key_auth, mcp_auth_header, _ = get_auth_context()
verbose_logger.debug(
f"MCP mcp_server_tool_call - User API Key Auth from context: {user_api_key_auth}"
)
@@ -213,15 +220,53 @@ if MCP_AVAILABLE:
############ Helper Functions ##########################
########################################################
+ async def _get_tools_from_mcp_servers(
+ user_api_key_auth: Optional[UserAPIKeyAuth],
+ mcp_auth_header: Optional[str],
+ mcp_servers: Optional[List[str]]
+ ) -> List[MCPTool]:
+ """
+ Helper method to fetch tools from MCP servers based on server filtering criteria.
+
+ Args:
+ user_api_key_auth: User authentication info for access control
+ mcp_auth_header: Optional auth header for MCP server
+ mcp_servers: Optional list of server names to filter by
+
+ Returns:
+ List[MCPTool]: List of tools from the specified or all allowed MCP servers
+ """
+ if mcp_servers:
+ # If mcp_servers header is present, only get tools from specified servers
+ tools = []
+ for server_id in await global_mcp_server_manager.get_allowed_mcp_servers(user_api_key_auth):
+ server = global_mcp_server_manager.get_mcp_server_by_id(server_id)
+ if server and any(normalize_server_name(server.name) == normalize_server_name(s) for s in mcp_servers):
+ server_tools = await global_mcp_server_manager._get_tools_from_server(
+ server=server,
+ mcp_auth_header=mcp_auth_header,
+ )
+ tools.extend(server_tools)
+ return tools
+ else:
+ # If no mcp_servers header, get tools from all allowed servers
+ return await global_mcp_server_manager.list_tools(
+ user_api_key_auth=user_api_key_auth,
+ mcp_auth_header=mcp_auth_header,
+ )
+
async def _list_mcp_tools(
user_api_key_auth: Optional[UserAPIKeyAuth] = None,
mcp_auth_header: Optional[str] = None,
+ mcp_servers: Optional[List[str]] = None,
) -> List[MCPTool]:
"""
List all available tools
Args:
user_api_key_auth: User authentication info for access control
+ mcp_auth_header: Optional auth header for MCP server
+ mcp_servers: Optional list of server names to filter by
"""
tools = []
for tool in global_mcp_tool_registry.list_tools():
@@ -236,12 +281,13 @@ if MCP_AVAILABLE:
"GLOBAL MCP TOOLS: %s", global_mcp_tool_registry.list_tools()
)
- tools_from_mcp_servers: List[MCPTool] = (
- await global_mcp_server_manager.list_tools(
- user_api_key_auth=user_api_key_auth,
- mcp_auth_header=mcp_auth_header,
- )
+ # Get tools from MCP servers
+ tools_from_mcp_servers = await _get_tools_from_mcp_servers(
+ user_api_key_auth=user_api_key_auth,
+ mcp_auth_header=mcp_auth_header,
+ mcp_servers=mcp_servers
)
+
verbose_logger.debug("TOOLS FROM MCP SERVERS: %s", tools_from_mcp_servers)
if tools_from_mcp_servers is not None:
tools.extend(tools_from_mcp_servers)
@@ -249,23 +295,27 @@ if MCP_AVAILABLE:
@client
async def call_mcp_tool(
- name: str,
- arguments: Optional[Dict[str, Any]] = None,
- user_api_key_auth: Optional[UserAPIKeyAuth] = None,
- mcp_auth_header: Optional[str] = None,
- **kwargs: Any
+ name: str,
+ arguments: Optional[Dict[str, Any]] = None,
+ user_api_key_auth: Optional[UserAPIKeyAuth] = None,
+ mcp_auth_header: Optional[str] = None,
+ **kwargs: Any
) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]:
"""
- Call a specific tool with the provided arguments
+ Call a specific tool with the provided arguments (handles prefixed tool names)
"""
if arguments is None:
raise HTTPException(
status_code=400, detail="Request arguments are required"
)
+ # Remove prefix from tool name for logging and processing
+ original_tool_name, server_name_from_prefix = get_server_name_prefix_tool_mcp(
+ name)
+
standard_logging_mcp_tool_call: StandardLoggingMCPToolCall = (
_get_standard_logging_mcp_tool_call(
- name=name,
+ name=original_tool_name, # Use original name for logging
arguments=arguments,
)
)
@@ -283,17 +333,17 @@ if MCP_AVAILABLE:
standard_logging_mcp_tool_call.get("mcp_server_name")
)
- # Try managed server tool first
+ # Try managed server tool first (pass the full prefixed name)
if name in global_mcp_server_manager.tool_name_to_mcp_server_name_mapping:
return await _handle_managed_mcp_tool(
- name=name,
+ name=name, # Pass the full name (potentially prefixed)
arguments=arguments,
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
)
- # Fall back to local tool registry
- return await _handle_local_mcp_tool(name, arguments)
+ # Fall back to local tool registry (use original name)
+ return await _handle_local_mcp_tool(original_tool_name, arguments)
def _get_standard_logging_mcp_tool_call(
name: str,
@@ -328,12 +378,15 @@ if MCP_AVAILABLE:
mcp_auth_header=mcp_auth_header,
)
verbose_logger.debug("CALL TOOL RESULT: %s", call_tool_result)
- return call_tool_result.content
+ return call_tool_result.content # type: ignore[return-value]
async def _handle_local_mcp_tool(
- name: str, arguments: Dict[str, Any]
+ name: str, arguments: Dict[str, Any]
) -> List[Union[MCPTextContent, MCPImageContent, MCPEmbeddedResource]]:
- """Handle tool execution for local registry tools"""
+ """
+ Handle tool execution for local registry tools
+ Note: Local tools don't use prefixes, so we use the original name
+ """
tool = global_mcp_tool_registry.get_tool(name)
if not tool:
raise HTTPException(status_code=404, detail=f"Tool '{name}' not found")
@@ -344,19 +397,22 @@ if MCP_AVAILABLE:
except Exception as e:
return [MCPTextContent(text=f"Error: {str(e)}", type="text")]
+
async def handle_streamable_http_mcp(
scope: Scope, receive: Receive, send: Send
) -> None:
"""Handle MCP requests through StreamableHTTP."""
try:
# Validate headers and log request info
- user_api_key_auth, mcp_auth_header = (
- await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
+ user_api_key_auth, mcp_auth_header, mcp_servers = (
+ await MCPRequestHandler.process_mcp_request(scope)
)
+ verbose_logger.debug(f"MCP request headers - mcp_servers: {mcp_servers}")
# Set the auth context variable for easy access in MCP functions
set_auth_context(
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
+ mcp_servers=mcp_servers,
)
# Ensure session managers are initialized
@@ -374,13 +430,14 @@ if MCP_AVAILABLE:
"""Handle MCP requests through SSE."""
try:
# Validate headers and log request info
- user_api_key_auth, mcp_auth_header = (
- await UserAPIKeyAuthMCP.user_api_key_auth_mcp(scope)
+ user_api_key_auth, mcp_auth_header, mcp_servers = (
+ await MCPRequestHandler.process_mcp_request(scope)
)
# Set the auth context variable for easy access in MCP functions
set_auth_context(
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
+ mcp_servers=mcp_servers,
)
# Ensure session managers are initialized
@@ -421,21 +478,27 @@ if MCP_AVAILABLE:
############ Auth Context Functions ####################
########################################################
- def set_auth_context(user_api_key_auth: UserAPIKeyAuth, mcp_auth_header: Optional[str] = None) -> None:
+ def set_auth_context(
+ user_api_key_auth: UserAPIKeyAuth,
+ mcp_auth_header: Optional[str] = None,
+ mcp_servers: Optional[List[str]] = None,
+ ) -> None:
"""
Set the UserAPIKeyAuth in the auth context variable.
Args:
user_api_key_auth: UserAPIKeyAuth object
mcp_auth_header: MCP auth header to be passed to the MCP server
+ mcp_servers: Optional list of server names to filter by
"""
- auth_user = LiteLLMAuthenticatedUser(
+ auth_user = MCPAuthenticatedUser(
user_api_key_auth=user_api_key_auth,
mcp_auth_header=mcp_auth_header,
+ mcp_servers=mcp_servers,
)
auth_context_var.set(auth_user)
- def get_auth_context() -> Tuple[Optional[UserAPIKeyAuth], Optional[str]]:
+ def get_auth_context() -> Tuple[Optional[UserAPIKeyAuth], Optional[str], Optional[List[str]]]:
"""
Get the UserAPIKeyAuth from the auth context variable.
@@ -443,9 +506,9 @@ if MCP_AVAILABLE:
Tuple[Optional[UserAPIKeyAuth], Optional[str]]: UserAPIKeyAuth object and MCP auth header
"""
auth_user = auth_context_var.get()
- if auth_user and isinstance(auth_user, LiteLLMAuthenticatedUser):
- return auth_user.user_api_key_auth, auth_user.mcp_auth_header
- return None, None
+ if auth_user and isinstance(auth_user, MCPAuthenticatedUser):
+ return auth_user.user_api_key_auth, auth_user.mcp_auth_header, auth_user.mcp_servers
+ return None, None, None
########################################################
############ End of Auth Context Functions #############
diff --git a/litellm/proxy/_experimental/mcp_server/utils.py b/litellm/proxy/_experimental/mcp_server/utils.py
index bad5f060fb8..ac969560e7c 100644
--- a/litellm/proxy/_experimental/mcp_server/utils.py
+++ b/litellm/proxy/_experimental/mcp_server/utils.py
@@ -1,5 +1,16 @@
+"""
+MCP Server Utilities
+"""
+from typing import Tuple
+
import importlib
+# Constants
+LITELLM_MCP_SERVER_NAME = "litellm-mcp-server"
+LITELLM_MCP_SERVER_VERSION = "1.0.0"
+LITELLM_MCP_SERVER_DESCRIPTION = "MCP Server for LiteLLM"
+MCP_TOOL_PREFIX_SEPARATOR = "/"
+MCP_TOOL_PREFIX_FORMAT = "{server_name}{separator}{tool_name}"
def is_mcp_available() -> bool:
"""
@@ -10,3 +21,56 @@ def is_mcp_available() -> bool:
return True
except ImportError:
return False
+
+def normalize_server_name(server_name: str) -> str:
+ """
+ Normalize server name by replacing spaces with underscores
+ """
+ return server_name.replace(" ", "_")
+
+def add_server_prefix_to_tool_name(tool_name: str, server_name: str) -> str:
+ """
+ Add server name prefix to tool name
+
+ Args:
+ tool_name: Original tool name
+ server_name: MCP server name
+
+ Returns:
+ Prefixed tool name in format: server_name::tool_name
+ """
+ formatted_server_name = normalize_server_name(server_name)
+
+ return MCP_TOOL_PREFIX_FORMAT.format(
+ server_name=formatted_server_name,
+ separator=MCP_TOOL_PREFIX_SEPARATOR,
+ tool_name=tool_name
+ )
+
+def get_server_name_prefix_tool_mcp(prefixed_tool_name: str) -> Tuple[str, str]:
+ """
+ Remove server name prefix from tool name
+
+ Args:
+ prefixed_tool_name: Tool name with server prefix
+
+ Returns:
+ Tuple of (original_tool_name, server_name)
+ """
+ if MCP_TOOL_PREFIX_SEPARATOR in prefixed_tool_name:
+ parts = prefixed_tool_name.split(MCP_TOOL_PREFIX_SEPARATOR, 1)
+ if len(parts) == 2:
+ return parts[1], parts[0] # tool_name, server_name
+ return prefixed_tool_name, "" # No prefix found, return original name
+
+def is_tool_name_prefixed(tool_name: str) -> bool:
+ """
+ Check if tool name has server prefix
+
+ Args:
+ tool_name: Tool name to check
+
+ Returns:
+ True if tool name is prefixed, False otherwise
+ """
+ return MCP_TOOL_PREFIX_SEPARATOR in tool_name
diff --git a/litellm/proxy/_experimental/out/_next/static/EUsvrfLmgLy71o8GGPdmU/_buildManifest.js b/litellm/proxy/_experimental/out/_next/static/S-QR4CNLVtfzm5EIYB2JM/_buildManifest.js
similarity index 100%
rename from litellm/proxy/_experimental/out/_next/static/EUsvrfLmgLy71o8GGPdmU/_buildManifest.js
rename to litellm/proxy/_experimental/out/_next/static/S-QR4CNLVtfzm5EIYB2JM/_buildManifest.js
diff --git a/litellm/proxy/_experimental/out/_next/static/EUsvrfLmgLy71o8GGPdmU/_ssgManifest.js b/litellm/proxy/_experimental/out/_next/static/S-QR4CNLVtfzm5EIYB2JM/_ssgManifest.js
similarity index 100%
rename from litellm/proxy/_experimental/out/_next/static/EUsvrfLmgLy71o8GGPdmU/_ssgManifest.js
rename to litellm/proxy/_experimental/out/_next/static/S-QR4CNLVtfzm5EIYB2JM/_ssgManifest.js
diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-b776bd9ac8911291.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-b776bd9ac8911291.js
deleted file mode 100644
index adac15afa67..00000000000
--- a/litellm/proxy/_experimental/out/_next/static/chunks/250-b776bd9ac8911291.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,{$D:function(){return eJ},$I:function(){return Q},$o:function(){return tF},AZ:function(){return Y},Au:function(){return e_},BL:function(){return eZ},Br:function(){return b},Bw:function(){return tx},E9:function(){return eY},EB:function(){return th},EG:function(){return eW},EY:function(){return e0},Eb:function(){return N},FC:function(){return eh},Gh:function(){return eG},H1:function(){return A},H2:function(){return r},Hx:function(){return ej},I1:function(){return S},It:function(){return O},J$:function(){return ec},JO:function(){return B},K8:function(){return h},K_:function(){return eQ},Ko:function(){return tN},LY:function(){return eV},Lp:function(){return eR},MO:function(){return p},Mx:function(){return tf},N3:function(){return eb},N8:function(){return ea},NL:function(){return e9},NV:function(){return m},Nc:function(){return eP},Nz:function(){return e1},O3:function(){return eD},OD:function(){return eC},OU:function(){return eu},Of:function(){return F},Og:function(){return y},Ou:function(){return ty},Ov:function(){return C},Oz:function(){return tO},PC:function(){return e6},PT:function(){return K},PY:function(){return tv},Pj:function(){return tt},Pv:function(){return tm},Qg:function(){return ex},RQ:function(){return j},Rg:function(){return et},Sb:function(){return ez},So:function(){return er},TF:function(){return tu},Tj:function(){return e2},Tx:function(){return tB},U8:function(){return te},UM:function(){return tl},VA:function(){return I},Vt:function(){return eX},W_:function(){return M},X:function(){return es},XB:function(){return tg},XO:function(){return T},Xd:function(){return eS},Xm:function(){return x},YU:function(){return eH},Yi:function(){return tj},Yo:function(){return U},Z9:function(){return V},Zr:function(){return k},a6:function(){return P},aC:function(){return tw},ao:function(){return eK},b1:function(){return ew},cq:function(){return R},cu:function(){return eA},e2:function(){return eT},eH:function(){return W},eW:function(){return tC},eZ:function(){return eO},fE:function(){return td},fP:function(){return eo},fk:function(){return tS},g:function(){return e4},gX:function(){return eB},gl:function(){return to},h3:function(){return ed},hT:function(){return ev},hy:function(){return f},ix:function(){return X},j2:function(){return ei},jA:function(){return e$},jE:function(){return eM},jr:function(){return tT},kK:function(){return g},kn:function(){return $},lP:function(){return w},lU:function(){return tr},lg:function(){return eN},mC:function(){return ti},mR:function(){return en},mY:function(){return tc},m_:function(){return D},mp:function(){return eq},n$:function(){return em},n9:function(){return ts},nJ:function(){return tb},nd:function(){return e7},o6:function(){return ee},oC:function(){return eF},ol:function(){return L},pf:function(){return eL},pu:function(){return tk},qI:function(){return _},qW:function(){return t_},qd:function(){return tE},qk:function(){return e8},qm:function(){return u},r1:function(){return tp},r6:function(){return G},rs:function(){return v},s0:function(){return Z},sN:function(){return eI},t$:function(){return J},t0:function(){return eE},t3:function(){return e5},tB:function(){return tn},tN:function(){return ep},u5:function(){return el},v9:function(){return ek},vh:function(){return eU},wX:function(){return E},wd:function(){return eg},xA:function(){return ey},xO:function(){return e3},xX:function(){return z},xZ:function(){return ta},zX:function(){return c},zg:function(){return ef}});var a=o(41021);let r=null;console.log=function(){};let n=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=window.location.origin,a=t||o;console.log("proxyBaseUrl:",r),console.log("serverRootPath:",e),e.length>0&&!a.endsWith(e)&&"/"!=e&&(a+=e,r=a),console.log("Updated proxyBaseUrl:",r)},c=()=>r||window.location.origin,s={GET:"GET",DELETE:"DELETE"},i=0,l=async e=>{let t=Date.now();t-i>6e4?(e.includes("Authentication Error - Expired Key")&&(a.ZP.info("UI Session Expired. Logging out."),i=t,document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=window.location.pathname),i=t):console.log("Error suppressed to prevent spam:",e)},d="Authorization";function h(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),d=e}let p=async()=>{console.log("Getting UI config");let e=await fetch("/.well-known/litellm-ui-config"),t=await e.json();return console.log("jsonData in getUiConfig:",t),n(t.server_root_path,t.proxy_base_url),t},w=async()=>{let e=r?"".concat(r,"/openapi.json"):"/openapi.json",t=await fetch(e);return await t.json()},u=async e=>{try{let t=r?"".concat(r,"/get/litellm_model_cost_map"):"/get/litellm_model_cost_map",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}}),a=await o.json();return console.log("received litellm model cost data: ".concat(a)),a}catch(e){throw console.error("Failed to get model cost map:",e),e}},g=async(e,t)=>{try{let o=r?"".concat(r,"/model/new"):"/model/new",n=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text()||"Network response was not ok";throw a.ZP.error(e),Error(e)}let c=await n.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}},f=async e=>{try{let t=r?"".concat(r,"/model/settings"):"/model/settings",o=await fetch(t,{method:"GET",headers:{[d]:"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=r?"".concat(r,"/model/delete"):"/model/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=r?"".concat(r,"/budget/delete"):"/budget/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=r?"".concat(r,"/budget/new"):"/budget/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=r?"".concat(r,"/budget/update"):"/budget/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t)=>{try{let o=r?"".concat(r,"/invitation/new"):"/invitation/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},j=async e=>{try{let t=r?"".concat(r,"/alerting/settings"):"/alerting/settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},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 a=r?"".concat(r,"/key/generate"):"/key/generate",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},C=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.auto_create_key=!1,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 a=r?"".concat(r,"/user/new"):"/user/new",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},S=async(e,t)=>{try{let o=r?"".concat(r,"/key/delete"):"/key/delete";console.log("in keyDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},N=async(e,t)=>{try{let o=r?"".concat(r,"/user/delete"):"/user/delete";console.log("in userDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete user(s):",e),e}},v=async(e,t)=>{try{let o=r?"".concat(r,"/team/delete"):"/team/delete";console.log("in teamDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},F=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,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,s=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,i=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,p=arguments.length>9&&void 0!==arguments[9]?arguments[9]:null;try{let w=r?"".concat(r,"/user/list"):"/user/list";console.log("in userListCall");let u=new URLSearchParams;if(t&&t.length>0){let e=t.join(",");u.append("user_ids",e)}o&&u.append("page",o.toString()),a&&u.append("page_size",a.toString()),n&&u.append("user_email",n),c&&u.append("role",c),s&&u.append("team",s),i&&u.append("sso_user_ids",i),h&&u.append("sort_by",h),p&&u.append("sort_order",p);let g=u.toString();g&&(w+="?".concat(g));let f=await fetch(w,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!f.ok){let e=await f.text();throw l(e),Error("Network response was not ok")}let y=await f.json();return console.log("/user/list API Response:",y),y}catch(e){throw console.error("Failed to create key:",e),e}},b=async function(e,t,o){let a=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,s=arguments.length>6&&void 0!==arguments[6]&&arguments[6];console.log("userInfoCall: ".concat(t,", ").concat(o,", ").concat(a,", ").concat(n,", ").concat(c,", ").concat(s));try{let i;if(a){i=r?"".concat(r,"/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=r?"".concat(r,"/user/info"):"/user/info",("Admin"!==o&&"Admin Viewer"!==o||s)&&t&&(i+="?user_id=".concat(t));console.log("Requesting user data from:",i);let h=await fetch(i,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("API Response:",p),p}catch(e){throw console.error("Failed to fetch user data:",e),e}},x=async(e,t)=>{try{let o=r?"".concat(r,"/team/info"):"/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},B=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;arguments.length>5&&void 0!==arguments[5]&&arguments[5],arguments.length>6&&void 0!==arguments[6]&&arguments[6],arguments.length>7&&void 0!==arguments[7]&&arguments[7],arguments.length>8&&void 0!==arguments[8]&&arguments[8];try{let c=r?"".concat(r,"/v2/team/list"):"/v2/team/list";console.log("in teamInfoCall");let s=new URLSearchParams;o&&s.append("user_id",o.toString()),t&&s.append("organization_id",t.toString()),a&&s.append("team_id",a.toString()),n&&s.append("team_alias",n.toString());let i=s.toString();i&&(c+="?".concat(i));let h=await fetch(c,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("/v2/team/list API Response:",p),p}catch(e){throw console.error("Failed to create key:",e),e}},O=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/team/list"):"/team/list";console.log("in teamInfoCall");let s=new URLSearchParams;o&&s.append("user_id",o.toString()),t&&s.append("organization_id",t.toString()),a&&s.append("team_id",a.toString()),n&&s.append("team_alias",n.toString());let i=s.toString();i&&(c+="?".concat(i));let h=await fetch(c,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("/team/list API Response:",p),p}catch(e){throw console.error("Failed to create key:",e),e}},P=async e=>{try{let t=r?"".concat(r,"/team/available"):"/team/available";console.log("in availableTeamListCall");let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("/team/available_teams API Response:",a),a}catch(e){throw e}},G=async e=>{try{let t=r?"".concat(r,"/organization/list"):"/organization/list",o=await fetch(t,{method:"GET",headers:{[d]:"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}},J=async(e,t)=>{try{let o=r?"".concat(r,"/organization/info"):"/organization/info";t&&(o="".concat(o,"?organization_id=").concat(t)),console.log("in teamInfoCall");let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},A=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=r?"".concat(r,"/organization/new"):"/organization/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},I=async(e,t)=>{try{console.log("Form Values in organizationUpdateCall:",t);let o=r?"".concat(r,"/organization/update"):"/organization/update",a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("Update Team Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},R=async(e,t)=>{try{let o=r?"".concat(r,"/organization/delete"):"/organization/delete",a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_ids:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Error deleting organization: ".concat(e))}return await a.json()}catch(e){throw console.error("Failed to delete organization:",e),e}},U=async(e,t)=>{try{let o=r?"".concat(r,"/utils/transform_request"):"/utils/transform_request",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to create key:",e),e}},z=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;try{let n=r?"".concat(r,"/user/daily/activity"):"/user/daily/activity",c=new URLSearchParams,s=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};c.append("start_date",s(t)),c.append("end_date",s(o)),c.append("page_size","1000"),c.append("page",a.toString());let i=c.toString();i&&(n+="?".concat(i));let h=await fetch(n,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!h.ok){let e=await h.text();throw l(e),Error("Network response was not ok")}return await h.json()}catch(e){throw console.error("Failed to create key:",e),e}},V=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/tag/daily/activity"):"/tag/daily/activity",s=new URLSearchParams,i=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};s.append("start_date",i(t)),s.append("end_date",i(o)),s.append("page_size","1000"),s.append("page",a.toString()),n&&s.append("tags",n.join(","));let h=s.toString();h&&(c+="?".concat(h));let p=await fetch(c,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!p.ok){let e=await p.text();throw l(e),Error("Network response was not ok")}return await p.json()}catch(e){throw console.error("Failed to create key:",e),e}},L=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/team/daily/activity"):"/team/daily/activity",s=new URLSearchParams,i=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};s.append("start_date",i(t)),s.append("end_date",i(o)),s.append("page_size","1000"),s.append("page",a.toString()),n&&s.append("team_ids",n.join(",")),s.append("exclude_team_ids","litellm-dashboard");let h=s.toString();h&&(c+="?".concat(h));let p=await fetch(c,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!p.ok){let e=await p.text();throw l(e),Error("Network response was not ok")}return await p.json()}catch(e){throw console.error("Failed to create key:",e),e}},M=async e=>{try{let t=r?"".concat(r,"/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}},D=async(e,t,o,a)=>{let n=r?"".concat(r,"/onboarding/claim_token"):"/onboarding/claim_token";try{let r=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({invitation_link:t,user_id:o,password:a})});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(c),c}catch(e){throw console.error("Failed to delete key:",e),e}},Z=async(e,t,o)=>{try{let a=r?"".concat(r,"/key/").concat(t,"/regenerate"):"/key/".concat(t,"/regenerate"),n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});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("Regenerate key Response:",c),c}catch(e){throw console.error("Failed to regenerate key:",e),e}},H=!1,q=null,Y=async(e,t,o)=>{try{console.log("modelInfoCall:",e,t,o);let n=r?"".concat(r,"/v2/model/info"):"/v2/model/info",c=new URLSearchParams;c.append("include_team_models","true"),c.toString()&&(n+="?".concat(c.toString()));let s=await fetch(n,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw e+="error shown=".concat(H),H||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),a.ZP.info(e,10),H=!0,q&&clearTimeout(q),q=setTimeout(()=>{H=!1},1e4)),Error("Network response was not ok")}let i=await s.json();return console.log("modelInfoCall:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},X=async(e,t)=>{try{let o=r?"".concat(r,"/v1/model/info"):"/v1/model/info";o+="?litellm_model_id=".concat(t);let a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok)throw await a.text(),Error("Network response was not ok");let n=await a.json();return console.log("modelInfoV1Call:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},$=async e=>{try{let t=r?"".concat(r,"/model_group/info"):"/model_group/info",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log("modelHubCall:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},K=async e=>{try{let t=r?"".concat(r,"/get/allowed_ips"):"/get/allowed_ips",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("getAllowedIPs:",a),a.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},W=async(e,t)=>{try{let o=r?"".concat(r,"/add/allowed_ip"):"/add/allowed_ip",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!a.ok){let e=await a.text();throw Error("Network response was not ok: ".concat(e))}let n=await a.json();return console.log("addAllowedIP:",n),n}catch(e){throw console.error("Failed to add allowed IP:",e),e}},Q=async(e,t)=>{try{let o=r?"".concat(r,"/delete/allowed_ip"):"/delete/allowed_ip",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!a.ok){let e=await a.text();throw Error("Network response was not ok: ".concat(e))}let n=await a.json();return console.log("deleteAllowedIP:",n),n}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},ee=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics"):"/model/metrics";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},et=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/model/streaming_metrics"):"/model/streaming_metrics";t&&(n="".concat(n,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(a));let c=await fetch(n,{method:"GET",headers:{[d]:"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}},eo=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics/slow_responses"):"/model/metrics/slow_responses";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},ea=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics/exceptions"):"/model/metrics/exceptions";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},er=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]&&arguments[3],n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,c=(arguments.length>5&&void 0!==arguments[5]&&arguments[5],arguments.length>6&&void 0!==arguments[6]&&arguments[6]);console.log("in /models calls, globalLitellmHeaderName",d);try{let t=r?"".concat(r,"/models"):"/models",o=new URLSearchParams;o.append("include_model_access_groups","True"),!0===a&&o.append("return_wildcard_routes","True"),!0===c&&o.append("only_model_access_groups","True"),n&&o.append("team_id",n.toString()),o.toString()&&(t+="?".concat(o.toString()));let s=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}return await s.json()}catch(e){throw console.error("Failed to create key:",e),e}},en=async e=>{try{let t=r?"".concat(r,"/global/spend/teams"):"/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ec=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/tags"):"/global/spend/tags";t&&o&&(n="".concat(n,"?start_date=").concat(t,"&end_date=").concat(o)),a&&(n+="".concat(n,"&tags=").concat(a.join(","))),console.log("in tagsSpendLogsCall:",n);let c=await fetch("".concat(n),{method:"GET",headers:{[d]:"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}},es=async e=>{try{let t=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ei=async e=>{try{let t=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},el=async(e,t)=>{try{let o=r?"".concat(r,"/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 a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to create key:",e),e}},ed=async(e,t,o,a,n,c,s,i,h,p,w)=>{try{let u=r?"".concat(r,"/spend/logs/ui"):"/spend/logs/ui",g=new URLSearchParams;t&&g.append("api_key",t),o&&g.append("team_id",o),a&&g.append("request_id",a),n&&g.append("start_date",n),c&&g.append("end_date",c),s&&g.append("page",s.toString()),i&&g.append("page_size",i.toString()),h&&g.append("user_id",h),p&&g.append("status_filter",p),w&&g.append("model",w);let f=g.toString();f&&(u+="?".concat(f));let y=await fetch(u,{method:"GET",headers:{[d]:"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 m=await y.json();return console.log("Spend Logs Response:",m),m}catch(e){throw console.error("Failed to fetch spend logs:",e),e}},eh=async e=>{try{let t=r?"".concat(r,"/global/spend/logs"):"/global/spend/logs",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ep=async e=>{try{let t=r?"".concat(r,"/global/spend/keys?limit=5"):"/global/spend/keys?limit=5",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ew=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/end_users"):"/global/spend/end_users",c="";c=t?JSON.stringify({api_key:t,startTime:o,endTime:a}):JSON.stringify({startTime:o,endTime:a});let s={method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:c},i=await fetch(n,s);if(!i.ok){let e=await i.text();throw l(e),Error("Network response was not ok")}let h=await i.json();return console.log(h),h}catch(e){throw console.error("Failed to create key:",e),e}},eu=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/provider"):"/global/spend/provider";o&&a&&(n+="?start_date=".concat(o,"&end_date=").concat(a)),t&&(n+="&api_key=".concat(t));let c={method:"GET",headers:{[d]:"Bearer ".concat(e)}},s=await fetch(n,c);if(!s.ok){let e=await s.text();throw l(e),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}},eg=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity"):"/global/activity";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},ef=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity/cache_hits"):"/global/activity/cache_hits";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},ey=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity/model"):"/global/activity/model";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},em=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/activity/exceptions"):"/global/activity/exceptions";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),a&&(n+="&model_group=".concat(a));let c={method:"GET",headers:{[d]:"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}},ek=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/activity/exceptions/deployment"):"/global/activity/exceptions/deployment";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),a&&(n+="&model_group=".concat(a));let c={method:"GET",headers:{[d]:"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}},e_=async e=>{try{let t=r?"".concat(r,"/global/spend/models?limit=5"):"/global/spend/models?limit=5",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},eT=async(e,t)=>{try{let o=r?"".concat(r,"/v2/key/info"):"/v2/key/info",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!a.ok){let e=await a.text();if(e.includes("Invalid proxy server token passed"))throw Error("Invalid proxy server token passed");throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},ej=async(e,t,o)=>{try{console.log("Sending model connection test request:",JSON.stringify(t));let n=r?"".concat(r,"/health/test_connection"):"/health/test_connection",c=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",[d]:"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 a;return{status:"error",message:(null===(a=i.error)||void 0===a?void 0:a.message)||"Connection test failed: ".concat(c.status," ").concat(c.statusText)}}}return i}catch(e){throw console.error("Model connection test error:",e),e}},eE=async(e,t)=>{try{console.log("entering keyInfoV1Call");let o=r?"".concat(r,"/key/info"):"/key/info";o="".concat(o,"?key=").concat(t);let n=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(console.log("response",n),!n.ok){let e=await n.text();l(e),a.ZP.error("Failed to fetch key info - "+e)}let c=await n.json();return console.log("data",c),c}catch(e){throw console.error("Failed to fetch key info:",e),e}},eC=async function(e,t,o,a,n,c,s,i){let h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,p=arguments.length>9&&void 0!==arguments[9]?arguments[9]:null;try{let w=r?"".concat(r,"/key/list"):"/key/list";console.log("in keyListCall");let u=new URLSearchParams;o&&u.append("team_id",o.toString()),t&&u.append("organization_id",t.toString()),a&&u.append("key_alias",a),c&&u.append("key_hash",c),n&&u.append("user_id",n.toString()),s&&u.append("page",s.toString()),i&&u.append("size",i.toString()),h&&u.append("sort_by",h),p&&u.append("sort_order",p),u.append("return_full_object","true"),u.append("include_team_keys","true");let g=u.toString();g&&(w+="?".concat(g));let f=await fetch(w,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!f.ok){let e=await f.text();throw l(e),Error("Network response was not ok")}let y=await f.json();return console.log("/team/list API Response:",y),y}catch(e){throw console.error("Failed to create key:",e),e}},eS=async(e,t)=>{try{let o=r?"".concat(r,"/user/get_users?role=").concat(t):"/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to get requested models:",e),e}},eN=async e=>{try{let t=r?"".concat(r,"/user/available_roles"):"/user/available_roles",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log("response from user/available_role",a),a}catch(e){throw e}},ev=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=r?"".concat(r,"/team/new"):"/team/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eF=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=r?"".concat(r,"/credentials"):"/credentials",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eb=async e=>{try{let t=r?"".concat(r,"/credentials"):"/credentials";console.log("in credentialListCall");let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("/credentials API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},ex=async(e,t,o)=>{try{let a=r?"".concat(r,"/credentials"):"/credentials";t?a+="/by_name/".concat(t):o&&(a+="/by_model/".concat(o)),console.log("in credentialListCall");let n=await fetch(a,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("/credentials API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eB=async(e,t)=>{try{let o=r?"".concat(r,"/credentials/").concat(t):"/credentials/".concat(t);console.log("in credentialDeleteCall:",t);let a=await fetch(o,{method:"DELETE",headers:{[d]:"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 n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},eO=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 a=r?"".concat(r,"/credentials/").concat(t):"/credentials/".concat(t),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...o})});if(!n.ok){let e=await n.text();throw l(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}},eP=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=r?"".concat(r,"/key/update"):"/key/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("Update key Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eG=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=r?"".concat(r,"/team/update"):"/team/update",n=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text();throw l(e),console.error("Error response from the server:",e),a.ZP.error("Failed to update team settings: "+e),Error(e)}let c=await n.json();return console.log("Update Team Response:",c),c}catch(e){throw console.error("Failed to update team:",e),e}},eJ=async(e,t,o)=>{try{console.log("Form Values in modelUpateCall:",t);let a=r?"".concat(r,"/model/").concat(o,"/update"):"/model/".concat(o,"/update"),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text();throw l(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("Update model Response:",c),c}catch(e){throw console.error("Failed to update model:",e),e}},eA=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let n=r?"".concat(r,"/team/member_add"):"/team/member_add",c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!c.ok){var a;let e=await c.text(),t={};try{t=JSON.parse(e)}catch(t){console.warn("Failed to parse error body as JSON:",e)}let o=(null==t?void 0:null===(a=t.detail)||void 0===a?void 0:a.error)||"Failed to add team member",r=Error(o);throw r.raw=t,r}let s=await c.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},eI=async(e,t,o)=>{try{console.log("Form Values in teamMemberUpdateCall:",o);let n=r?"".concat(r,"/team/member_update"):"/team/member_update",c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!c.ok){var a;let e=await c.text(),t={};try{t=JSON.parse(e)}catch(t){console.warn("Failed to parse error body as JSON:",e)}let o=(null==t?void 0:null===(a=t.detail)||void 0===a?void 0:a.error)||"Failed to add team member",r=Error(o);throw r.raw=t,r}let s=await c.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to update team member:",e),e}},eR=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let a=r?"".concat(r,"/team/member_delete"):"/team/member_delete",n=await fetch(a,{method:"POST",headers:{[d]:"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 l(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}},eU=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let a=r?"".concat(r,"/organization/member_add"):"/organization/member_add",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!n.ok){let e=await n.text();throw l(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}},ez=async(e,t,o)=>{try{console.log("Form Values in organizationMemberDeleteCall:",o);let a=r?"".concat(r,"/organization/member_delete"):"/organization/member_delete",n=await fetch(a,{method:"DELETE",headers:{[d]:"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 l(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}},eV=async(e,t,o)=>{try{console.log("Form Values in organizationMemberUpdateCall:",o);let a=r?"".concat(r,"/organization/member_update"):"/organization/member_update",n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},eL=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let a=r?"".concat(r,"/user/update"):"/user/update",n={...t};null!==o&&(n.user_role=o),n=JSON.stringify(n);let c=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:n});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}},eM=async(e,t)=>{try{let o=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw l(e),Error(e)}let c=await n.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}},eD=async e=>{try{let t=r?"".concat(r,"/budget/list"):"/budget/list",o=await fetch(t,{method:"GET",headers:{[d]:"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,t,o)=>{try{let t=r?"".concat(r,"/get/config/callbacks"):"/get/config/callbacks",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eH=async e=>{try{let t=r?"".concat(r,"/config/list?config_type=general_settings"):"/config/list?config_type=general_settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eq=async e=>{try{let t=r?"".concat(r,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eY=async(e,t)=>{try{let o=r?"".concat(r,"/config/field/info?field_name=").concat(t):"/config/field/info?field_name=".concat(t),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok)throw await a.text(),Error("Network response was not ok");return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eX=async(e,t)=>{try{let o=r?"".concat(r,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e$=async(e,t,o)=>{try{let n=r?"".concat(r,"/config/field/update"):"/config/field/update",c=await fetch(n,{method:"POST",headers:{[d]:"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}},eK=async(e,t)=>{try{let o=r?"".concat(r,"/config/field/delete"):"/config/field/delete",n=await fetch(o,{method:"POST",headers:{[d]:"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 l(e),Error("Network response was not ok")}let c=await n.json();return a.ZP.success("Field reset on proxy"),c}catch(e){throw console.error("Failed to get callbacks:",e),e}},eW=async(e,t)=>{try{let o=r?"".concat(r,"/config/pass_through_endpoint?endpoint_id=").concat(t):"/config/pass_through_endpoint".concat(t),a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},eQ=async(e,t)=>{try{let o=r?"".concat(r,"/config/update"):"/config/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e0=async e=>{try{let t=r?"".concat(r,"/health"):"/health",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e1=async(e,t)=>{try{let o=r?"".concat(r,"/health?model=").concat(encodeURIComponent(t)):"/health?model=".concat(encodeURIComponent(t)),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw Error(e||"Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to call /health for model ".concat(t,":"),e),e}},e2=async e=>{try{let t=r?"".concat(r,"/cache/ping"):"/cache/ping",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e3=async e=>{try{let t=r?"".concat(r,"/health/latest"):"/health/latest",o=await fetch(t,{method:"GET",headers:{[d]:"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 /health/latest:",e),e}},e4=async e=>{try{console.log("Getting proxy UI settings"),console.log("proxyBaseUrl in getProxyUISettings:",r);let t=r?"".concat(r,"/sso/get/ui_settings"):"/sso/get/ui_settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e5=async e=>{try{let t=r?"".concat(r,"/v2/guardrails/list"):"/v2/guardrails/list",o=await fetch(t,{method:"GET",headers:{[d]:"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 guardrails list:",e),e}},e6=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails"):"/guardrails",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({guardrail:t})});if(!a.ok){let e=await a.text();throw l(e),Error(e)}let n=await a.json();return console.log("Create guardrail response:",n),n}catch(e){throw console.error("Failed to create guardrail:",e),e}},e8=async(e,t,o)=>{try{let a=r?"".concat(r,"/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:",a);let n=await fetch(a,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("Fetched log details:",c),c}catch(e){throw console.error("Failed to fetch log details:",e),e}},e9=async e=>{try{let t=r?"".concat(r,"/get/internal_user_settings"):"/get/internal_user_settings";console.log("Fetching SSO settings from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched SSO settings:",a),a}catch(e){throw console.error("Failed to fetch SSO settings:",e),e}},e7=async(e,t)=>{try{let o=r?"".concat(r,"/update/internal_user_settings"):"/update/internal_user_settings";console.log("Updating internal user settings:",t);let n=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});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("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}},te=async e=>{try{let t=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server";console.log("Fetching MCP servers from:",t);let o=await fetch(t,{method:s.GET,headers:{[d]:"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 a=await o.json();return console.log("Fetched MCP servers:",a),a}catch(e){throw console.error("Failed to fetch MCP servers:",e),e}},tt=async(e,t)=>{try{console.log("Form Values in createMCPServer:",t);let o=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},to=async(e,t)=>{try{let o=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server",a=await fetch(o,{method:"PUT",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to update MCP server:",e),e}},ta=async(e,t)=>{try{let o=(r?"".concat(r):"")+"/v1/mcp/server/".concat(t);console.log("in deleteMCPServer:",t);let a=await fetch(o,{method:s.DELETE,headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}}catch(e){throw console.error("Failed to delete key:",e),e}},tr=async(e,t)=>{try{let o=r?"".concat(r,"/mcp-rest/tools/list?server_id=").concat(t):"/mcp-rest/tools/list?server_id=".concat(t);console.log("Fetching MCP tools from:",o);let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log("Fetched MCP tools:",n),n}catch(e){throw console.error("Failed to fetch MCP tools:",e),e}},tn=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/mcp-rest/tools/call"):"/mcp-rest/tools/call";console.log("Calling MCP tool:",t,"with arguments:",o);let c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"x-mcp-auth":a,"Content-Type":"application/json"},body:JSON.stringify({name:t,arguments:o})});if(!c.ok){let e=await c.text();throw l(e),Error("Network response was not ok")}let s=await c.json();return console.log("MCP tool call response:",s),s}catch(e){throw console.error("Failed to call MCP tool:",e),e}},tc=async(e,t)=>{try{let o=r?"".concat(r,"/tag/new"):"/tag/new",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error creating tag:",e),e}},ts=async(e,t)=>{try{let o=r?"".concat(r,"/tag/update"):"/tag/update",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error updating tag:",e),e}},ti=async(e,t)=>{try{let o=r?"".concat(r,"/tag/info"):"/tag/info",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({names:t})});if(!a.ok){let e=await a.text();return await l(e),{}}return await a.json()}catch(e){throw console.error("Error getting tag info:",e),e}},tl=async e=>{try{let t=r?"".concat(r,"/tag/list"):"/tag/list",o=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e)}});if(!o.ok){let e=await o.text();return await l(e),{}}return await o.json()}catch(e){throw console.error("Error listing tags:",e),e}},td=async(e,t)=>{try{let o=r?"".concat(r,"/tag/delete"):"/tag/delete",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({name:t})});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error deleting tag:",e),e}},th=async e=>{try{let t=r?"".concat(r,"/get/default_team_settings"):"/get/default_team_settings";console.log("Fetching default team settings from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched default team settings:",a),a}catch(e){throw console.error("Failed to fetch default team settings:",e),e}},tp=async(e,t)=>{try{let o=r?"".concat(r,"/update/default_team_settings"):"/update/default_team_settings";console.log("Updating default team settings:",t);let n=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});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("Updated default team settings:",c),a.ZP.success("Default team settings updated successfully"),c}catch(e){throw console.error("Failed to update default team settings:",e),e}},tw=async(e,t)=>{try{let o=r?"".concat(r,"/team/permissions_list?team_id=").concat(t):"/team/permissions_list?team_id=".concat(t),a=await fetch(o,{method:"GET",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log("Team permissions response:",n),n}catch(e){throw console.error("Failed to get team permissions:",e),e}},tu=async(e,t,o)=>{try{let a=r?"".concat(r,"/team/permissions_update"):"/team/permissions_update",n=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({team_id:t,team_member_permissions:o})});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("Team permissions response:",c),c}catch(e){throw console.error("Failed to update team permissions:",e),e}},tg=async(e,t)=>{try{let o=r?"".concat(r,"/spend/logs/session/ui?session_id=").concat(encodeURIComponent(t)):"/spend/logs/session/ui?session_id=".concat(encodeURIComponent(t)),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to fetch session logs:",e),e}},tf=async(e,t)=>{try{let o=r?"".concat(r,"/vector_store/new"):"/vector_store/new",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.json();throw Error(e.detail||"Failed to create vector store")}return await a.json()}catch(e){throw console.error("Error creating vector store:",e),e}},ty=async function(e){arguments.length>1&&void 0!==arguments[1]&&arguments[1],arguments.length>2&&void 0!==arguments[2]&&arguments[2];try{let t=r?"".concat(r,"/vector_store/list"):"/vector_store/list",o=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)}});if(!o.ok){let e=await o.json();throw Error(e.detail||"Failed to list vector stores")}return await o.json()}catch(e){throw console.error("Error listing vector stores:",e),e}},tm=async(e,t)=>{try{let o=r?"".concat(r,"/vector_store/delete"):"/vector_store/delete",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({vector_store_id:t})});if(!a.ok){let e=await a.json();throw Error(e.detail||"Failed to delete vector store")}return await a.json()}catch(e){throw console.error("Error deleting vector store:",e),e}},tk=async e=>{try{let t=r?"".concat(r,"/email/event_settings"):"/email/event_settings",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get email event settings")}let a=await o.json();return console.log("Email event settings response:",a),a}catch(e){throw console.error("Failed to get email event settings:",e),e}},t_=async(e,t)=>{try{let o=r?"".concat(r,"/email/event_settings"):"/email/event_settings",a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Failed to update email event settings")}let n=await a.json();return console.log("Update email event settings response:",n),n}catch(e){throw console.error("Failed to update email event settings:",e),e}},tT=async e=>{try{let t=r?"".concat(r,"/email/event_settings/reset"):"/email/event_settings/reset",o=await fetch(t,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to reset email event settings")}let a=await o.json();return console.log("Reset email event settings response:",a),a}catch(e){throw console.error("Failed to reset email event settings:",e),e}},tj=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails/").concat(t):"/guardrails/".concat(t),a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error(e)}let n=await a.json();return console.log("Delete guardrail response:",n),n}catch(e){throw console.error("Failed to delete guardrail:",e),e}},tE=async e=>{try{let t=r?"".concat(r,"/guardrails/ui/add_guardrail_settings"):"/guardrails/ui/add_guardrail_settings",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get guardrail UI settings")}let a=await o.json();return console.log("Guardrail UI settings response:",a),a}catch(e){throw console.error("Failed to get guardrail UI settings:",e),e}},tC=async e=>{try{let t=r?"".concat(r,"/guardrails/ui/provider_specific_params"):"/guardrails/ui/provider_specific_params",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get guardrail provider specific parameters")}let a=await o.json();return console.log("Guardrail provider specific params response:",a),a}catch(e){throw console.error("Failed to get guardrail provider specific parameters:",e),e}},tS=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails/").concat(t,"/info"):"/guardrails/".concat(t,"/info"),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Failed to get guardrail info")}let n=await a.json();return console.log("Guardrail info response:",n),n}catch(e){throw console.error("Failed to get guardrail info:",e),e}},tN=async(e,t,o)=>{try{let a=r?"".concat(r,"/guardrails/").concat(t):"/guardrails/".concat(t),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let e=await n.text();throw l(e),Error("Failed to update guardrail")}let c=await n.json();return console.log("Update guardrail response:",c),c}catch(e){throw console.error("Failed to update guardrail:",e),e}},tv=async e=>{try{let t=r?"".concat(r,"/get/sso_settings"):"/get/sso_settings";console.log("Fetching SSO configuration from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched SSO configuration:",a),a}catch(e){throw console.error("Failed to fetch SSO configuration:",e),e}},tF=async(e,t)=>{try{let o=r?"".concat(r,"/update/sso_settings"):"/update/sso_settings";console.log("Updating SSO configuration:",t);let a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log("Updated SSO configuration:",n),n}catch(e){throw console.error("Failed to update SSO configuration:",e),e}},tb=async(e,t,o,a,n)=>{try{let t=r?"".concat(r,"/audit"):"/audit",o=new URLSearchParams;a&&o.append("page",a.toString()),n&&o.append("page_size",n.toString());let c=o.toString();c&&(t+="?".concat(c));let s=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}return await s.json()}catch(e){throw console.error("Failed to fetch audit logs:",e),e}},tx=async e=>{try{let t=r?"".concat(r,"/user/available_users"):"/user/available_users",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e)}});if(!o.ok){if(404===o.status)return null;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 fetch remaining users:",e),e}},tB=async(e,t,o)=>{try{let n=r?"".concat(r,"/config/pass_through_endpoint/").concat(encodeURIComponent(t)):"/config/pass_through_endpoint/".concat(encodeURIComponent(t)),c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});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("Pass through endpoint updated successfully"),s}catch(e){throw console.error("Failed to update pass through endpoint:",e),e}},tO=async(e,t)=>{try{let o=r?"".concat(r,"/config/callback/delete"):"/config/callback/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({callback_name:t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to delete specific callback:",e),e}}}}]);
\ No newline at end of file
diff --git a/litellm/proxy/_experimental/out/_next/static/chunks/250-ef2b37352f059268.js b/litellm/proxy/_experimental/out/_next/static/chunks/250-ef2b37352f059268.js
new file mode 100644
index 00000000000..0a1940f912a
--- /dev/null
+++ b/litellm/proxy/_experimental/out/_next/static/chunks/250-ef2b37352f059268.js
@@ -0,0 +1 @@
+"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[250],{19250:function(e,t,o){o.d(t,{$D:function(){return eA},$I:function(){return ee},$o:function(){return tb},AZ:function(){return X},Au:function(){return eT},BL:function(){return eH},Br:function(){return x},Bw:function(){return tO},E9:function(){return eX},EB:function(){return tp},EG:function(){return eQ},EY:function(){return e1},Eb:function(){return v},FC:function(){return ep},Gh:function(){return eJ},H1:function(){return I},H2:function(){return r},Hx:function(){return ej},I1:function(){return F},It:function(){return P},J$:function(){return es},JO:function(){return B},K8:function(){return h},K_:function(){return e0},Ko:function(){return tv},LY:function(){return eL},Lp:function(){return eU},MO:function(){return p},Mx:function(){return ty},N3:function(){return ex},N8:function(){return er},NL:function(){return e7},NV:function(){return m},Nc:function(){return eG},Nz:function(){return e2},O3:function(){return eZ},OD:function(){return eS},OU:function(){return eg},Of:function(){return b},Og:function(){return y},Ou:function(){return tm},Ov:function(){return S},Oz:function(){return tP},PC:function(){return e8},PT:function(){return W},PY:function(){return tN},Pj:function(){return to},Pv:function(){return tk},Qg:function(){return eO},RQ:function(){return E},Rg:function(){return eo},Sb:function(){return eV},So:function(){return en},TF:function(){return tg},Tj:function(){return e3},Tx:function(){return tB},U8:function(){return tt},UM:function(){return td},VA:function(){return R},Vt:function(){return e$},W_:function(){return D},X:function(){return ei},XB:function(){return tf},XO:function(){return T},Xd:function(){return eF},Xm:function(){return O},YU:function(){return eq},Yi:function(){return tj},Yo:function(){return z},Z9:function(){return L},Zr:function(){return k},a6:function(){return G},aC:function(){return tu},ao:function(){return eW},b1:function(){return eu},cq:function(){return U},cu:function(){return eI},e2:function(){return eE},eH:function(){return Q},eW:function(){return tS},eZ:function(){return eP},fE:function(){return th},fP:function(){return ea},fk:function(){return tF},g:function(){return e5},gX:function(){return eB},gl:function(){return ta},h3:function(){return eh},hT:function(){return eN},hy:function(){return f},ix:function(){return $},j2:function(){return el},jA:function(){return eK},jE:function(){return eD},jr:function(){return tE},kK:function(){return g},kn:function(){return K},lP:function(){return w},lU:function(){return tn},lg:function(){return ev},mC:function(){return tl},mR:function(){return ec},mY:function(){return ts},m_:function(){return Z},mp:function(){return eY},n$:function(){return ek},n9:function(){return ti},nJ:function(){return tx},nd:function(){return te},o6:function(){return et},oC:function(){return eb},ol:function(){return M},pf:function(){return eM},pu:function(){return t_},qI:function(){return _},qW:function(){return tT},qd:function(){return tC},qk:function(){return e9},qm:function(){return u},r1:function(){return tw},r6:function(){return J},rs:function(){return N},s0:function(){return H},sN:function(){return eR},t$:function(){return A},t0:function(){return eC},t3:function(){return e6},tB:function(){return tc},tN:function(){return ew},u5:function(){return ed},uz:function(){return j},v9:function(){return e_},vh:function(){return ez},wX:function(){return C},wd:function(){return ef},xA:function(){return em},xO:function(){return e4},xX:function(){return V},xZ:function(){return tr},zX:function(){return c},zg:function(){return ey}});var a=o(41021);let r=null;console.log=function(){};let n=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=window.location.origin,a=t||o;console.log("proxyBaseUrl:",r),console.log("serverRootPath:",e),e.length>0&&!a.endsWith(e)&&"/"!=e&&(a+=e,r=a),console.log("Updated proxyBaseUrl:",r)},c=()=>r||window.location.origin,s={GET:"GET",DELETE:"DELETE"},i=0,l=async e=>{let t=Date.now();t-i>6e4?(e.includes("Authentication Error - Expired Key")&&(a.ZP.info("UI Session Expired. Logging out."),i=t,document.cookie="token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;",window.location.href=window.location.pathname),i=t):console.log("Error suppressed to prevent spam:",e)},d="Authorization";function h(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"Authorization";console.log("setGlobalLitellmHeaderName: ".concat(e)),d=e}let p=async()=>{console.log("Getting UI config");let e=await fetch("/litellm/.well-known/litellm-ui-config"),t=await e.json();return console.log("jsonData in getUiConfig:",t),n(t.server_root_path,t.proxy_base_url),t},w=async()=>{let e=r?"".concat(r,"/openapi.json"):"/openapi.json",t=await fetch(e);return await t.json()},u=async e=>{try{let t=r?"".concat(r,"/get/litellm_model_cost_map"):"/get/litellm_model_cost_map",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}}),a=await o.json();return console.log("received litellm model cost data: ".concat(a)),a}catch(e){throw console.error("Failed to get model cost map:",e),e}},g=async(e,t)=>{try{let o=r?"".concat(r,"/model/new"):"/model/new",n=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text()||"Network response was not ok";throw a.ZP.error(e),Error(e)}let c=await n.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}},f=async e=>{try{let t=r?"".concat(r,"/model/settings"):"/model/settings",o=await fetch(t,{method:"GET",headers:{[d]:"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=r?"".concat(r,"/model/delete"):"/model/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},m=async(e,t)=>{if(console.log("budget_id in budget delete call: ".concat(t)),null!=e)try{let o=r?"".concat(r,"/budget/delete"):"/budget/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({id:t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},k=async(e,t)=>{try{console.log("Form Values in budgetCreateCall:",t),console.log("Form Values after check:",t);let o=r?"".concat(r,"/budget/new"):"/budget/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},_=async(e,t)=>{try{console.log("Form Values in budgetUpdateCall:",t),console.log("Form Values after check:",t);let o=r?"".concat(r,"/budget/update"):"/budget/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},T=async(e,t)=>{try{let o=r?"".concat(r,"/invitation/new"):"/invitation/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t})});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 n=await a.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=r?"".concat(r,"/alerting/settings"):"/alerting/settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},j=async(e,t)=>{try{if(console.log("Form Values in keyCreateServiceAccountCall:",t),t.description&&(t.metadata||(t.metadata={}),t.metadata.description=t.description,delete t.description,t.metadata=JSON.stringify(t.metadata)),t.metadata){console.log("formValues.metadata:",t.metadata);try{t.metadata=JSON.parse(t.metadata)}catch(e){throw Error("Failed to parse metadata: "+e)}}console.log("Form Values after check:",t);let o=r?"".concat(r,"/key/service-account/generate"):"/key/service-account/generate",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!a.ok){let e=await a.text();throw l(e),console.error("Error response from the server:",e),Error(e)}let n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},C=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 a=r?"".concat(r,"/key/generate"):"/key/generate",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},S=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.auto_create_key=!1,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 a=r?"".concat(r,"/user/new"):"/user/new",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},F=async(e,t)=>{try{let o=r?"".concat(r,"/key/delete"):"/key/delete";console.log("in keyDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},v=async(e,t)=>{try{let o=r?"".concat(r,"/user/delete"):"/user/delete";console.log("in userDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({user_ids:t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete user(s):",e),e}},N=async(e,t)=>{try{let o=r?"".concat(r,"/team/delete"):"/team/delete";console.log("in teamDeleteCall:",t);let a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_ids:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},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,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,c=arguments.length>5&&void 0!==arguments[5]?arguments[5]:null,s=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,i=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,p=arguments.length>9&&void 0!==arguments[9]?arguments[9]:null;try{let w=r?"".concat(r,"/user/list"):"/user/list";console.log("in userListCall");let u=new URLSearchParams;if(t&&t.length>0){let e=t.join(",");u.append("user_ids",e)}o&&u.append("page",o.toString()),a&&u.append("page_size",a.toString()),n&&u.append("user_email",n),c&&u.append("role",c),s&&u.append("team",s),i&&u.append("sso_user_ids",i),h&&u.append("sort_by",h),p&&u.append("sort_order",p);let g=u.toString();g&&(w+="?".concat(g));let f=await fetch(w,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!f.ok){let e=await f.text();throw l(e),Error("Network response was not ok")}let y=await f.json();return console.log("/user/list API Response:",y),y}catch(e){throw console.error("Failed to create key:",e),e}},x=async function(e,t,o){let a=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,s=arguments.length>6&&void 0!==arguments[6]&&arguments[6];console.log("userInfoCall: ".concat(t,", ").concat(o,", ").concat(a,", ").concat(n,", ").concat(c,", ").concat(s));try{let i;if(a){i=r?"".concat(r,"/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=r?"".concat(r,"/user/info"):"/user/info",("Admin"!==o&&"Admin Viewer"!==o||s)&&t&&(i+="?user_id=".concat(t));console.log("Requesting user data from:",i);let h=await fetch(i,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("API Response:",p),p}catch(e){throw console.error("Failed to fetch user data:",e),e}},O=async(e,t)=>{try{let o=r?"".concat(r,"/team/info"):"/team/info";t&&(o="".concat(o,"?team_id=").concat(t)),console.log("in teamInfoCall");let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},B=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;arguments.length>5&&void 0!==arguments[5]&&arguments[5],arguments.length>6&&void 0!==arguments[6]&&arguments[6],arguments.length>7&&void 0!==arguments[7]&&arguments[7],arguments.length>8&&void 0!==arguments[8]&&arguments[8];try{let c=r?"".concat(r,"/v2/team/list"):"/v2/team/list";console.log("in teamInfoCall");let s=new URLSearchParams;o&&s.append("user_id",o.toString()),t&&s.append("organization_id",t.toString()),a&&s.append("team_id",a.toString()),n&&s.append("team_alias",n.toString());let i=s.toString();i&&(c+="?".concat(i));let h=await fetch(c,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("/v2/team/list API Response:",p),p}catch(e){throw console.error("Failed to create key:",e),e}},P=async function(e,t){let o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/team/list"):"/team/list";console.log("in teamInfoCall");let s=new URLSearchParams;o&&s.append("user_id",o.toString()),t&&s.append("organization_id",t.toString()),a&&s.append("team_id",a.toString()),n&&s.append("team_alias",n.toString());let i=s.toString();i&&(c+="?".concat(i));let h=await fetch(c,{method:"GET",headers:{[d]:"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 p=await h.json();return console.log("/team/list API Response:",p),p}catch(e){throw console.error("Failed to create key:",e),e}},G=async e=>{try{let t=r?"".concat(r,"/team/available"):"/team/available";console.log("in availableTeamListCall");let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("/team/available_teams API Response:",a),a}catch(e){throw e}},J=async e=>{try{let t=r?"".concat(r,"/organization/list"):"/organization/list",o=await fetch(t,{method:"GET",headers:{[d]:"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}},A=async(e,t)=>{try{let o=r?"".concat(r,"/organization/info"):"/organization/info";t&&(o="".concat(o,"?organization_id=").concat(t)),console.log("in teamInfoCall");let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},I=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=r?"".concat(r,"/organization/new"):"/organization/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},R=async(e,t)=>{try{console.log("Form Values in organizationUpdateCall:",t);let o=r?"".concat(r,"/organization/update"):"/organization/update",a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("Update Team Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},U=async(e,t)=>{try{let o=r?"".concat(r,"/organization/delete"):"/organization/delete",a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_ids:[t]})});if(!a.ok){let e=await a.text();throw l(e),Error("Error deleting organization: ".concat(e))}return await a.json()}catch(e){throw console.error("Failed to delete organization:",e),e}},z=async(e,t)=>{try{let o=r?"".concat(r,"/utils/transform_request"):"/utils/transform_request",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to create key:",e),e}},V=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1;try{let n=r?"".concat(r,"/user/daily/activity"):"/user/daily/activity",c=new URLSearchParams,s=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};c.append("start_date",s(t)),c.append("end_date",s(o)),c.append("page_size","1000"),c.append("page",a.toString());let i=c.toString();i&&(n+="?".concat(i));let h=await fetch(n,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!h.ok){let e=await h.text();throw l(e),Error("Network response was not ok")}return await h.json()}catch(e){throw console.error("Failed to create key:",e),e}},L=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/tag/daily/activity"):"/tag/daily/activity",s=new URLSearchParams,i=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};s.append("start_date",i(t)),s.append("end_date",i(o)),s.append("page_size","1000"),s.append("page",a.toString()),n&&s.append("tags",n.join(","));let h=s.toString();h&&(c+="?".concat(h));let p=await fetch(c,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!p.ok){let e=await p.text();throw l(e),Error("Network response was not ok")}return await p.json()}catch(e){throw console.error("Failed to create key:",e),e}},M=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:1,n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;try{let c=r?"".concat(r,"/team/daily/activity"):"/team/daily/activity",s=new URLSearchParams,i=e=>{let t=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),a=String(e.getDate()).padStart(2,"0");return"".concat(t,"-").concat(o,"-").concat(a)};s.append("start_date",i(t)),s.append("end_date",i(o)),s.append("page_size","1000"),s.append("page",a.toString()),n&&s.append("team_ids",n.join(",")),s.append("exclude_team_ids","litellm-dashboard");let h=s.toString();h&&(c+="?".concat(h));let p=await fetch(c,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!p.ok){let e=await p.text();throw l(e),Error("Network response was not ok")}return await p.json()}catch(e){throw console.error("Failed to create key:",e),e}},D=async e=>{try{let t=r?"".concat(r,"/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}},Z=async(e,t,o,a)=>{let n=r?"".concat(r,"/onboarding/claim_token"):"/onboarding/claim_token";try{let r=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({invitation_link:t,user_id:o,password:a})});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(c),c}catch(e){throw console.error("Failed to delete key:",e),e}},H=async(e,t,o)=>{try{let a=r?"".concat(r,"/key/").concat(t,"/regenerate"):"/key/".concat(t,"/regenerate"),n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});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("Regenerate key Response:",c),c}catch(e){throw console.error("Failed to regenerate key:",e),e}},q=!1,Y=null,X=async(e,t,o)=>{try{console.log("modelInfoCall:",e,t,o);let n=r?"".concat(r,"/v2/model/info"):"/v2/model/info",c=new URLSearchParams;c.append("include_team_models","true"),c.toString()&&(n+="?".concat(c.toString()));let s=await fetch(n,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw e+="error shown=".concat(q),q||(e.includes("No model list passed")&&(e="No Models Exist. Click Add Model to get started."),a.ZP.info(e,10),q=!0,Y&&clearTimeout(Y),Y=setTimeout(()=>{q=!1},1e4)),Error("Network response was not ok")}let i=await s.json();return console.log("modelInfoCall:",i),i}catch(e){throw console.error("Failed to create key:",e),e}},$=async(e,t)=>{try{let o=r?"".concat(r,"/v1/model/info"):"/v1/model/info";o+="?litellm_model_id=".concat(t);let a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok)throw await a.text(),Error("Network response was not ok");let n=await a.json();return console.log("modelInfoV1Call:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},K=async e=>{try{let t=r?"".concat(r,"/model_group/info"):"/model_group/info",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log("modelHubCall:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},W=async e=>{try{let t=r?"".concat(r,"/get/allowed_ips"):"/get/allowed_ips",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("getAllowedIPs:",a),a.data}catch(e){throw console.error("Failed to get allowed IPs:",e),e}},Q=async(e,t)=>{try{let o=r?"".concat(r,"/add/allowed_ip"):"/add/allowed_ip",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!a.ok){let e=await a.text();throw Error("Network response was not ok: ".concat(e))}let n=await a.json();return console.log("addAllowedIP:",n),n}catch(e){throw console.error("Failed to add allowed IP:",e),e}},ee=async(e,t)=>{try{let o=r?"".concat(r,"/delete/allowed_ip"):"/delete/allowed_ip",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({ip:t})});if(!a.ok){let e=await a.text();throw Error("Network response was not ok: ".concat(e))}let n=await a.json();return console.log("deleteAllowedIP:",n),n}catch(e){throw console.error("Failed to delete allowed IP:",e),e}},et=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics"):"/model/metrics";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},eo=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/model/streaming_metrics"):"/model/streaming_metrics";t&&(n="".concat(n,"?_selected_model_group=").concat(t,"&startTime=").concat(o,"&endTime=").concat(a));let c=await fetch(n,{method:"GET",headers:{[d]:"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}},ea=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics/slow_responses"):"/model/metrics/slow_responses";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},er=async(e,t,o,a,n,c,s,i)=>{try{let t=r?"".concat(r,"/model/metrics/exceptions"):"/model/metrics/exceptions";a&&(t="".concat(t,"?_selected_model_group=").concat(a,"&startTime=").concat(n,"&endTime=").concat(c,"&api_key=").concat(s,"&customer=").concat(i));let o=await fetch(t,{method:"GET",headers:{[d]:"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}},en=async function(e,t,o){let a=arguments.length>3&&void 0!==arguments[3]&&arguments[3],n=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null,c=(arguments.length>5&&void 0!==arguments[5]&&arguments[5],arguments.length>6&&void 0!==arguments[6]&&arguments[6]);console.log("in /models calls, globalLitellmHeaderName",d);try{let t=r?"".concat(r,"/models"):"/models",o=new URLSearchParams;o.append("include_model_access_groups","True"),!0===a&&o.append("return_wildcard_routes","True"),!0===c&&o.append("only_model_access_groups","True"),n&&o.append("team_id",n.toString()),o.toString()&&(t+="?".concat(o.toString()));let s=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}return await s.json()}catch(e){throw console.error("Failed to create key:",e),e}},ec=async e=>{try{let t=r?"".concat(r,"/global/spend/teams"):"/global/spend/teams";console.log("in teamSpendLogsCall:",t);let o=await fetch("".concat(t),{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},es=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/tags"):"/global/spend/tags";t&&o&&(n="".concat(n,"?start_date=").concat(t,"&end_date=").concat(o)),a&&(n+="".concat(n,"&tags=").concat(a.join(","))),console.log("in tagsSpendLogsCall:",n);let c=await fetch("".concat(n),{method:"GET",headers:{[d]:"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}},ei=async e=>{try{let t=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},el=async e=>{try{let t=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ed=async(e,t)=>{try{let o=r?"".concat(r,"/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 a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to create key:",e),e}},eh=async(e,t,o,a,n,c,s,i,h,p,w)=>{try{let u=r?"".concat(r,"/spend/logs/ui"):"/spend/logs/ui",g=new URLSearchParams;t&&g.append("api_key",t),o&&g.append("team_id",o),a&&g.append("request_id",a),n&&g.append("start_date",n),c&&g.append("end_date",c),s&&g.append("page",s.toString()),i&&g.append("page_size",i.toString()),h&&g.append("user_id",h),p&&g.append("status_filter",p),w&&g.append("model",w);let f=g.toString();f&&(u+="?".concat(f));let y=await fetch(u,{method:"GET",headers:{[d]:"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 m=await y.json();return console.log("Spend Logs Response:",m),m}catch(e){throw console.error("Failed to fetch spend logs:",e),e}},ep=async e=>{try{let t=r?"".concat(r,"/global/spend/logs"):"/global/spend/logs",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},ew=async e=>{try{let t=r?"".concat(r,"/global/spend/keys?limit=5"):"/global/spend/keys?limit=5",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},eu=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/end_users"):"/global/spend/end_users",c="";c=t?JSON.stringify({api_key:t,startTime:o,endTime:a}):JSON.stringify({startTime:o,endTime:a});let s={method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:c},i=await fetch(n,s);if(!i.ok){let e=await i.text();throw l(e),Error("Network response was not ok")}let h=await i.json();return console.log(h),h}catch(e){throw console.error("Failed to create key:",e),e}},eg=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/spend/provider"):"/global/spend/provider";o&&a&&(n+="?start_date=".concat(o,"&end_date=").concat(a)),t&&(n+="&api_key=".concat(t));let c={method:"GET",headers:{[d]:"Bearer ".concat(e)}},s=await fetch(n,c);if(!s.ok){let e=await s.text();throw l(e),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}},ef=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity"):"/global/activity";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},ey=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity/cache_hits"):"/global/activity/cache_hits";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},em=async(e,t,o)=>{try{let a=r?"".concat(r,"/global/activity/model"):"/global/activity/model";t&&o&&(a+="?start_date=".concat(t,"&end_date=").concat(o));let n={method:"GET",headers:{[d]:"Bearer ".concat(e)}},c=await fetch(a,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}},ek=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/activity/exceptions"):"/global/activity/exceptions";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),a&&(n+="&model_group=".concat(a));let c={method:"GET",headers:{[d]:"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}},e_=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/global/activity/exceptions/deployment"):"/global/activity/exceptions/deployment";t&&o&&(n+="?start_date=".concat(t,"&end_date=").concat(o)),a&&(n+="&model_group=".concat(a));let c={method:"GET",headers:{[d]:"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}},eT=async e=>{try{let t=r?"".concat(r,"/global/spend/models?limit=5"):"/global/spend/models?limit=5",o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log(a),a}catch(e){throw console.error("Failed to create key:",e),e}},eE=async(e,t)=>{try{let o=r?"".concat(r,"/v2/key/info"):"/v2/key/info",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({keys:t})});if(!a.ok){let e=await a.text();if(e.includes("Invalid proxy server token passed"))throw Error("Invalid proxy server token passed");throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to create key:",e),e}},ej=async(e,t,o)=>{try{console.log("Sending model connection test request:",JSON.stringify(t));let n=r?"".concat(r,"/health/test_connection"):"/health/test_connection",c=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",[d]:"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 a;return{status:"error",message:(null===(a=i.error)||void 0===a?void 0:a.message)||"Connection test failed: ".concat(c.status," ").concat(c.statusText)}}}return i}catch(e){throw console.error("Model connection test error:",e),e}},eC=async(e,t)=>{try{console.log("entering keyInfoV1Call");let o=r?"".concat(r,"/key/info"):"/key/info";o="".concat(o,"?key=").concat(t);let n=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(console.log("response",n),!n.ok){let e=await n.text();l(e),a.ZP.error("Failed to fetch key info - "+e)}let c=await n.json();return console.log("data",c),c}catch(e){throw console.error("Failed to fetch key info:",e),e}},eS=async function(e,t,o,a,n,c,s,i){let h=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null,p=arguments.length>9&&void 0!==arguments[9]?arguments[9]:null;try{let w=r?"".concat(r,"/key/list"):"/key/list";console.log("in keyListCall");let u=new URLSearchParams;o&&u.append("team_id",o.toString()),t&&u.append("organization_id",t.toString()),a&&u.append("key_alias",a),c&&u.append("key_hash",c),n&&u.append("user_id",n.toString()),s&&u.append("page",s.toString()),i&&u.append("size",i.toString()),h&&u.append("sort_by",h),p&&u.append("sort_order",p),u.append("return_full_object","true"),u.append("include_team_keys","true");let g=u.toString();g&&(w+="?".concat(g));let f=await fetch(w,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!f.ok){let e=await f.text();throw l(e),Error("Network response was not ok")}let y=await f.json();return console.log("/team/list API Response:",y),y}catch(e){throw console.error("Failed to create key:",e),e}},eF=async(e,t)=>{try{let o=r?"".concat(r,"/user/get_users?role=").concat(t):"/user/get_users?role=".concat(t);console.log("in userGetAllUsersCall:",o);let a=await fetch(o,{method:"GET",headers:{[d]:"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 n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to get requested models:",e),e}},ev=async e=>{try{let t=r?"".concat(r,"/user/available_roles"):"/user/available_roles",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok)throw await o.text(),Error("Network response was not ok");let a=await o.json();return console.log("response from user/available_role",a),a}catch(e){throw e}},eN=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=r?"".concat(r,"/team/new"):"/team/new",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eb=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=r?"".concat(r,"/credentials"):"/credentials",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},ex=async e=>{try{let t=r?"".concat(r,"/credentials"):"/credentials";console.log("in credentialListCall");let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("/credentials API Response:",a),a}catch(e){throw console.error("Failed to create key:",e),e}},eO=async(e,t,o)=>{try{let a=r?"".concat(r,"/credentials"):"/credentials";t?a+="/by_name/".concat(t):o&&(a+="/by_model/".concat(o)),console.log("in credentialListCall");let n=await fetch(a,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("/credentials API Response:",c),c}catch(e){throw console.error("Failed to create key:",e),e}},eB=async(e,t)=>{try{let o=r?"".concat(r,"/credentials/").concat(t):"/credentials/".concat(t);console.log("in credentialDeleteCall:",t);let a=await fetch(o,{method:"DELETE",headers:{[d]:"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 n=await a.json();return console.log(n),n}catch(e){throw console.error("Failed to delete key:",e),e}},eP=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 a=r?"".concat(r,"/credentials/").concat(t):"/credentials/".concat(t),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...o})});if(!n.ok){let e=await n.text();throw l(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}},eG=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=r?"".concat(r,"/key/update"):"/key/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("Update key Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},eJ=async(e,t)=>{try{console.log("Form Values in teamUpateCall:",t);let o=r?"".concat(r,"/team/update"):"/team/update",n=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text();throw l(e),console.error("Error response from the server:",e),a.ZP.error("Failed to update team settings: "+e),Error(e)}let c=await n.json();return console.log("Update Team Response:",c),c}catch(e){throw console.error("Failed to update team:",e),e}},eA=async(e,t,o)=>{try{console.log("Form Values in modelUpateCall:",t);let a=r?"".concat(r,"/model/").concat(o,"/update"):"/model/".concat(o,"/update"),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!n.ok){let e=await n.text();throw l(e),console.error("Error update from the server:",e),Error("Network response was not ok")}let c=await n.json();return console.log("Update model Response:",c),c}catch(e){throw console.error("Failed to update model:",e),e}},eI=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let n=r?"".concat(r,"/team/member_add"):"/team/member_add",c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,member:o})});if(!c.ok){var a;let e=await c.text(),t={};try{t=JSON.parse(e)}catch(t){console.warn("Failed to parse error body as JSON:",e)}let o=(null==t?void 0:null===(a=t.detail)||void 0===a?void 0:a.error)||"Failed to add team member",r=Error(o);throw r.raw=t,r}let s=await c.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to create key:",e),e}},eR=async(e,t,o)=>{try{console.log("Form Values in teamMemberUpdateCall:",o);let n=r?"".concat(r,"/team/member_update"):"/team/member_update",c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({team_id:t,role:o.role,user_id:o.user_id})});if(!c.ok){var a;let e=await c.text(),t={};try{t=JSON.parse(e)}catch(t){console.warn("Failed to parse error body as JSON:",e)}let o=(null==t?void 0:null===(a=t.detail)||void 0===a?void 0:a.error)||"Failed to add team member",r=Error(o);throw r.raw=t,r}let s=await c.json();return console.log("API Response:",s),s}catch(e){throw console.error("Failed to update team member:",e),e}},eU=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let a=r?"".concat(r,"/team/member_delete"):"/team/member_delete",n=await fetch(a,{method:"POST",headers:{[d]:"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 l(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}},ez=async(e,t,o)=>{try{console.log("Form Values in teamMemberAddCall:",o);let a=r?"".concat(r,"/organization/member_add"):"/organization/member_add",n=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,member:o})});if(!n.ok){let e=await n.text();throw l(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}},eV=async(e,t,o)=>{try{console.log("Form Values in organizationMemberDeleteCall:",o);let a=r?"".concat(r,"/organization/member_delete"):"/organization/member_delete",n=await fetch(a,{method:"DELETE",headers:{[d]:"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 l(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}},eL=async(e,t,o)=>{try{console.log("Form Values in organizationMemberUpdateCall:",o);let a=r?"".concat(r,"/organization/member_update"):"/organization/member_update",n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({organization_id:t,...o})});if(!n.ok){let e=await n.text();throw l(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}},eM=async(e,t,o)=>{try{console.log("Form Values in userUpdateUserCall:",t);let a=r?"".concat(r,"/user/update"):"/user/update",n={...t};null!==o&&(n.user_role=o),n=JSON.stringify(n);let c=await fetch(a,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:n});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}},eD=async(e,t)=>{try{let o=r?"".concat(r,"/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:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!n.ok){let e=await n.text();throw l(e),Error(e)}let c=await n.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}},eZ=async e=>{try{let t=r?"".concat(r,"/budget/list"):"/budget/list",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eH=async(e,t,o)=>{try{let t=r?"".concat(r,"/get/config/callbacks"):"/get/config/callbacks",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eq=async e=>{try{let t=r?"".concat(r,"/config/list?config_type=general_settings"):"/config/list?config_type=general_settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eY=async e=>{try{let t=r?"".concat(r,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",o=await fetch(t,{method:"GET",headers:{[d]:"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}},eX=async(e,t)=>{try{let o=r?"".concat(r,"/config/field/info?field_name=").concat(t):"/config/field/info?field_name=".concat(t),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok)throw await a.text(),Error("Network response was not ok");return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e$=async(e,t)=>{try{let o=r?"".concat(r,"/config/pass_through_endpoint"):"/config/pass_through_endpoint",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},eK=async(e,t,o)=>{try{let n=r?"".concat(r,"/config/field/update"):"/config/field/update",c=await fetch(n,{method:"POST",headers:{[d]:"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}},eW=async(e,t)=>{try{let o=r?"".concat(r,"/config/field/delete"):"/config/field/delete",n=await fetch(o,{method:"POST",headers:{[d]:"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 l(e),Error("Network response was not ok")}let c=await n.json();return a.ZP.success("Field reset on proxy"),c}catch(e){throw console.error("Failed to get callbacks:",e),e}},eQ=async(e,t)=>{try{let o=r?"".concat(r,"/config/pass_through_endpoint?endpoint_id=").concat(t):"/config/pass_through_endpoint".concat(t),a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to get callbacks:",e),e}},e0=async(e,t)=>{try{let o=r?"".concat(r,"/config/update"):"/config/update",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to set callbacks:",e),e}},e1=async e=>{try{let t=r?"".concat(r,"/health"):"/health",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e2=async(e,t)=>{try{let o=r?"".concat(r,"/health?model=").concat(encodeURIComponent(t)):"/health?model=".concat(encodeURIComponent(t)),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw Error(e||"Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to call /health for model ".concat(t,":"),e),e}},e3=async e=>{try{let t=r?"".concat(r,"/cache/ping"):"/cache/ping",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e4=async e=>{try{let t=r?"".concat(r,"/health/latest"):"/health/latest",o=await fetch(t,{method:"GET",headers:{[d]:"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 /health/latest:",e),e}},e5=async e=>{try{console.log("Getting proxy UI settings"),console.log("proxyBaseUrl in getProxyUISettings:",r);let t=r?"".concat(r,"/sso/get/ui_settings"):"/sso/get/ui_settings",o=await fetch(t,{method:"GET",headers:{[d]:"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}},e6=async e=>{try{let t=r?"".concat(r,"/v2/guardrails/list"):"/v2/guardrails/list",o=await fetch(t,{method:"GET",headers:{[d]:"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 guardrails list:",e),e}},e8=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails"):"/guardrails",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({guardrail:t})});if(!a.ok){let e=await a.text();throw l(e),Error(e)}let n=await a.json();return console.log("Create guardrail response:",n),n}catch(e){throw console.error("Failed to create guardrail:",e),e}},e9=async(e,t,o)=>{try{let a=r?"".concat(r,"/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:",a);let n=await fetch(a,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});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("Fetched log details:",c),c}catch(e){throw console.error("Failed to fetch log details:",e),e}},e7=async e=>{try{let t=r?"".concat(r,"/get/internal_user_settings"):"/get/internal_user_settings";console.log("Fetching SSO settings from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched SSO settings:",a),a}catch(e){throw console.error("Failed to fetch SSO settings:",e),e}},te=async(e,t)=>{try{let o=r?"".concat(r,"/update/internal_user_settings"):"/update/internal_user_settings";console.log("Updating internal user settings:",t);let n=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});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("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}},tt=async e=>{try{let t=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server";console.log("Fetching MCP servers from:",t);let o=await fetch(t,{method:s.GET,headers:{[d]:"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 a=await o.json();return console.log("Fetched MCP servers:",a),a}catch(e){throw console.error("Failed to fetch MCP servers:",e),e}},to=async(e,t)=>{try{console.log("Form Values in createMCPServer:",t);let o=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({...t})});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 n=await a.json();return console.log("API Response:",n),n}catch(e){throw console.error("Failed to create key:",e),e}},ta=async(e,t)=>{try{let o=r?"".concat(r,"/v1/mcp/server"):"/v1/mcp/server",a=await fetch(o,{method:"PUT",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to update MCP server:",e),e}},tr=async(e,t)=>{try{let o=(r?"".concat(r):"")+"/v1/mcp/server/".concat(t);console.log("in deleteMCPServer:",t);let a=await fetch(o,{method:s.DELETE,headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}}catch(e){throw console.error("Failed to delete key:",e),e}},tn=async(e,t)=>{try{let o=r?"".concat(r,"/mcp-rest/tools/list?server_id=").concat(t):"/mcp-rest/tools/list?server_id=".concat(t);console.log("Fetching MCP tools from:",o);let a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}}),n=await a.json();if(console.log("Fetched MCP tools response:",n),!a.ok){if(n.error&&n.message)throw Error(n.message);throw Error("Failed to fetch MCP tools")}return n}catch(e){return console.error("Failed to fetch MCP tools:",e),{tools:[],error:"network_error",message:e instanceof Error?e.message:"Failed to fetch MCP tools"}}},tc=async(e,t,o,a)=>{try{let n=r?"".concat(r,"/mcp-rest/tools/call"):"/mcp-rest/tools/call";console.log("Calling MCP tool:",t,"with arguments:",o);let c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"x-mcp-auth":a,"Content-Type":"application/json"},body:JSON.stringify({name:t,arguments:o})});if(!c.ok){let e=await c.text();throw l(e),Error("Network response was not ok")}let s=await c.json();return console.log("MCP tool call response:",s),s}catch(e){throw console.error("Failed to call MCP tool:",e),e}},ts=async(e,t)=>{try{let o=r?"".concat(r,"/tag/new"):"/tag/new",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error creating tag:",e),e}},ti=async(e,t)=>{try{let o=r?"".concat(r,"/tag/update"):"/tag/update",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error updating tag:",e),e}},tl=async(e,t)=>{try{let o=r?"".concat(r,"/tag/info"):"/tag/info",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({names:t})});if(!a.ok){let e=await a.text();return await l(e),{}}return await a.json()}catch(e){throw console.error("Error getting tag info:",e),e}},td=async e=>{try{let t=r?"".concat(r,"/tag/list"):"/tag/list",o=await fetch(t,{method:"GET",headers:{Authorization:"Bearer ".concat(e)}});if(!o.ok){let e=await o.text();return await l(e),{}}return await o.json()}catch(e){throw console.error("Error listing tags:",e),e}},th=async(e,t)=>{try{let o=r?"".concat(r,"/tag/delete"):"/tag/delete",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({name:t})});if(!a.ok){let e=await a.text();await l(e);return}return await a.json()}catch(e){throw console.error("Error deleting tag:",e),e}},tp=async e=>{try{let t=r?"".concat(r,"/get/default_team_settings"):"/get/default_team_settings";console.log("Fetching default team settings from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched default team settings:",a),a}catch(e){throw console.error("Failed to fetch default team settings:",e),e}},tw=async(e,t)=>{try{let o=r?"".concat(r,"/update/default_team_settings"):"/update/default_team_settings";console.log("Updating default team settings:",t);let n=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});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("Updated default team settings:",c),a.ZP.success("Default team settings updated successfully"),c}catch(e){throw console.error("Failed to update default team settings:",e),e}},tu=async(e,t)=>{try{let o=r?"".concat(r,"/team/permissions_list?team_id=").concat(t):"/team/permissions_list?team_id=".concat(t),a=await fetch(o,{method:"GET",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log("Team permissions response:",n),n}catch(e){throw console.error("Failed to get team permissions:",e),e}},tg=async(e,t,o)=>{try{let a=r?"".concat(r,"/team/permissions_update"):"/team/permissions_update",n=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({team_id:t,team_member_permissions:o})});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("Team permissions response:",c),c}catch(e){throw console.error("Failed to update team permissions:",e),e}},tf=async(e,t)=>{try{let o=r?"".concat(r,"/spend/logs/session/ui?session_id=").concat(encodeURIComponent(t)):"/spend/logs/session/ui?session_id=".concat(encodeURIComponent(t)),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to fetch session logs:",e),e}},ty=async(e,t)=>{try{let o=r?"".concat(r,"/vector_store/new"):"/vector_store/new",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify(t)});if(!a.ok){let e=await a.json();throw Error(e.detail||"Failed to create vector store")}return await a.json()}catch(e){throw console.error("Error creating vector store:",e),e}},tm=async function(e){arguments.length>1&&void 0!==arguments[1]&&arguments[1],arguments.length>2&&void 0!==arguments[2]&&arguments[2];try{let t=r?"".concat(r,"/vector_store/list"):"/vector_store/list",o=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)}});if(!o.ok){let e=await o.json();throw Error(e.detail||"Failed to list vector stores")}return await o.json()}catch(e){throw console.error("Error listing vector stores:",e),e}},tk=async(e,t)=>{try{let o=r?"".concat(r,"/vector_store/delete"):"/vector_store/delete",a=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer ".concat(e)},body:JSON.stringify({vector_store_id:t})});if(!a.ok){let e=await a.json();throw Error(e.detail||"Failed to delete vector store")}return await a.json()}catch(e){throw console.error("Error deleting vector store:",e),e}},t_=async e=>{try{let t=r?"".concat(r,"/email/event_settings"):"/email/event_settings",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get email event settings")}let a=await o.json();return console.log("Email event settings response:",a),a}catch(e){throw console.error("Failed to get email event settings:",e),e}},tT=async(e,t)=>{try{let o=r?"".concat(r,"/email/event_settings"):"/email/event_settings",a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Failed to update email event settings")}let n=await a.json();return console.log("Update email event settings response:",n),n}catch(e){throw console.error("Failed to update email event settings:",e),e}},tE=async e=>{try{let t=r?"".concat(r,"/email/event_settings/reset"):"/email/event_settings/reset",o=await fetch(t,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to reset email event settings")}let a=await o.json();return console.log("Reset email event settings response:",a),a}catch(e){throw console.error("Failed to reset email event settings:",e),e}},tj=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails/").concat(t):"/guardrails/".concat(t),a=await fetch(o,{method:"DELETE",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error(e)}let n=await a.json();return console.log("Delete guardrail response:",n),n}catch(e){throw console.error("Failed to delete guardrail:",e),e}},tC=async e=>{try{let t=r?"".concat(r,"/guardrails/ui/add_guardrail_settings"):"/guardrails/ui/add_guardrail_settings",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get guardrail UI settings")}let a=await o.json();return console.log("Guardrail UI settings response:",a),a}catch(e){throw console.error("Failed to get guardrail UI settings:",e),e}},tS=async e=>{try{let t=r?"".concat(r,"/guardrails/ui/provider_specific_params"):"/guardrails/ui/provider_specific_params",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!o.ok){let e=await o.text();throw l(e),Error("Failed to get guardrail provider specific parameters")}let a=await o.json();return console.log("Guardrail provider specific params response:",a),a}catch(e){throw console.error("Failed to get guardrail provider specific parameters:",e),e}},tF=async(e,t)=>{try{let o=r?"".concat(r,"/guardrails/").concat(t,"/info"):"/guardrails/".concat(t,"/info"),a=await fetch(o,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!a.ok){let e=await a.text();throw l(e),Error("Failed to get guardrail info")}let n=await a.json();return console.log("Guardrail info response:",n),n}catch(e){throw console.error("Failed to get guardrail info:",e),e}},tv=async(e,t,o)=>{try{let a=r?"".concat(r,"/guardrails/").concat(t):"/guardrails/".concat(t),n=await fetch(a,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});if(!n.ok){let e=await n.text();throw l(e),Error("Failed to update guardrail")}let c=await n.json();return console.log("Update guardrail response:",c),c}catch(e){throw console.error("Failed to update guardrail:",e),e}},tN=async e=>{try{let t=r?"".concat(r,"/get/sso_settings"):"/get/sso_settings";console.log("Fetching SSO configuration from:",t);let o=await fetch(t,{method:"GET",headers:{[d]:"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 a=await o.json();return console.log("Fetched SSO configuration:",a),a}catch(e){throw console.error("Failed to fetch SSO configuration:",e),e}},tb=async(e,t)=>{try{let o=r?"".concat(r,"/update/sso_settings"):"/update/sso_settings";console.log("Updating SSO configuration:",t);let a=await fetch(o,{method:"PATCH",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(t)});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}let n=await a.json();return console.log("Updated SSO configuration:",n),n}catch(e){throw console.error("Failed to update SSO configuration:",e),e}},tx=async(e,t,o,a,n)=>{try{let t=r?"".concat(r,"/audit"):"/audit",o=new URLSearchParams;a&&o.append("page",a.toString()),n&&o.append("page_size",n.toString());let c=o.toString();c&&(t+="?".concat(c));let s=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"}});if(!s.ok){let e=await s.text();throw l(e),Error("Network response was not ok")}return await s.json()}catch(e){throw console.error("Failed to fetch audit logs:",e),e}},tO=async e=>{try{let t=r?"".concat(r,"/user/available_users"):"/user/available_users",o=await fetch(t,{method:"GET",headers:{[d]:"Bearer ".concat(e)}});if(!o.ok){if(404===o.status)return null;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 fetch remaining users:",e),e}},tB=async(e,t,o)=>{try{let n=r?"".concat(r,"/config/pass_through_endpoint/").concat(encodeURIComponent(t)):"/config/pass_through_endpoint/".concat(encodeURIComponent(t)),c=await fetch(n,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify(o)});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("Pass through endpoint updated successfully"),s}catch(e){throw console.error("Failed to update pass through endpoint:",e),e}},tP=async(e,t)=>{try{let o=r?"".concat(r,"/config/callback/delete"):"/config/callback/delete",a=await fetch(o,{method:"POST",headers:{[d]:"Bearer ".concat(e),"Content-Type":"application/json"},body:JSON.stringify({callback_name:t})});if(!a.ok){let e=await a.text();throw l(e),Error("Network response was not ok")}return await a.json()}catch(e){throw console.error("Failed to delete specific callback:",e),e}}}}]);
\ No newline at end of file
diff --git a/ui/litellm-dashboard/out/_next/static/chunks/313-fe5a1ed341fdff45.js b/litellm/proxy/_experimental/out/_next/static/chunks/313-cf4a28394ee560d6.js
similarity index 99%
rename from ui/litellm-dashboard/out/_next/static/chunks/313-fe5a1ed341fdff45.js
rename to litellm/proxy/_experimental/out/_next/static/chunks/313-cf4a28394ee560d6.js
index a2fed077ce7..d635038a052 100644
--- a/ui/litellm-dashboard/out/_next/static/chunks/313-fe5a1ed341fdff45.js
+++ b/litellm/proxy/_experimental/out/_next/static/chunks/313-cf4a28394ee560d6.js
@@ -1 +1 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[313],{23639:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var a=n(1119),r=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"}}]},name:"copy",theme:"outlined"},o=n(55015),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},77565:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var a=n(1119),r=n(2265),i={icon:{tag:"svg",attrs:{viewBox:"64 64 896 896",focusable:"false"},children:[{tag:"path",attrs:{d:"M765.7 486.8L314.9 134.7A7.97 7.97 0 00302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 000-50.4z"}}]},name:"right",theme:"outlined"},o=n(55015),s=r.forwardRef(function(e,t){return r.createElement(o.Z,(0,a.Z)({},e,{ref:t,icon:i}))})},12485:function(e,t,n){"use strict";n.d(t,{Z:function(){return p}});var a=n(5853),r=n(31492),i=n(26898),o=n(97324),s=n(1153),l=n(2265),c=n(35242),u=n(42698);n(64016),n(8710),n(33232);let d=(0,s.fn)("Tab"),p=l.forwardRef((e,t)=>{let{icon:n,className:p,children:g}=e,m=(0,a._T)(e,["icon","className","children"]),b=(0,l.useContext)(c.O),f=(0,l.useContext)(u.Z);return l.createElement(r.O,Object.assign({ref:t,className:(0,o.q)(d("root"),"flex whitespace-nowrap truncate max-w-xs outline-none focus:ring-0 text-tremor-default transition duration-100",f?(0,s.bM)(f,i.K.text).selectTextColor:"solid"===b?"ui-selected:text-tremor-content-emphasis dark:ui-selected:text-dark-tremor-content-emphasis":"ui-selected:text-tremor-brand dark:ui-selected:text-dark-tremor-brand",function(e,t){switch(e){case"line":return(0,o.q)("ui-selected:border-b-2 hover:border-b-2 border-transparent transition duration-100 -mb-px px-2 py-2","hover:border-tremor-content hover:text-tremor-content-emphasis text-tremor-content","dark:hover:border-dark-tremor-content-emphasis dark:hover:text-dark-tremor-content-emphasis dark:text-dark-tremor-content",t?(0,s.bM)(t,i.K.border).selectBorderColor:"ui-selected:border-tremor-brand dark:ui-selected:border-dark-tremor-brand");case"solid":return(0,o.q)("border-transparent border rounded-tremor-small px-2.5 py-1","ui-selected:border-tremor-border ui-selected:bg-tremor-background ui-selected:shadow-tremor-input hover:text-tremor-content-emphasis ui-selected:text-tremor-brand","dark:ui-selected:border-dark-tremor-border dark:ui-selected:bg-dark-tremor-background dark:ui-selected:shadow-dark-tremor-input dark:hover:text-dark-tremor-content-emphasis dark:ui-selected:text-dark-tremor-brand",t?(0,s.bM)(t,i.K.text).selectTextColor:"text-tremor-content dark:text-dark-tremor-content")}}(b,f),p)},m),n?l.createElement(n,{className:(0,o.q)(d("icon"),"flex-none h-5 w-5",g?"mr-2":"")}):null,g?l.createElement("span",null,g):null)});p.displayName="Tab"},18135:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var a=n(5853),r=n(31492),i=n(97324),o=n(1153),s=n(2265);let l=(0,o.fn)("TabGroup"),c=s.forwardRef((e,t)=>{let{defaultIndex:n,index:o,onIndexChange:c,children:u,className:d}=e,p=(0,a._T)(e,["defaultIndex","index","onIndexChange","children","className"]);return s.createElement(r.O.Group,Object.assign({as:"div",ref:t,defaultIndex:n,selectedIndex:o,onChange:c,className:(0,i.q)(l("root"),"w-full",d)},p),u)});c.displayName="TabGroup"},35242:function(e,t,n){"use strict";n.d(t,{O:function(){return c},Z:function(){return d}});var a=n(5853),r=n(2265),i=n(42698);n(64016),n(8710),n(33232);var o=n(31492),s=n(97324);let l=(0,n(1153).fn)("TabList"),c=(0,r.createContext)("line"),u={line:(0,s.q)("flex border-b space-x-4","border-tremor-border","dark:border-dark-tremor-border"),solid:(0,s.q)("inline-flex p-0.5 rounded-tremor-default space-x-1.5","bg-tremor-background-subtle","dark:bg-dark-tremor-background-subtle")},d=r.forwardRef((e,t)=>{let{color:n,variant:d="line",children:p,className:g}=e,m=(0,a._T)(e,["color","variant","children","className"]);return r.createElement(o.O.List,Object.assign({ref:t,className:(0,s.q)(l("root"),"justify-start overflow-x-clip",u[d],g)},m),r.createElement(c.Provider,{value:d},r.createElement(i.Z.Provider,{value:n},p)))});d.displayName="TabList"},29706:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var a=n(5853);n(42698);var r=n(64016);n(8710);var i=n(33232),o=n(97324),s=n(1153),l=n(2265);let c=(0,s.fn)("TabPanel"),u=l.forwardRef((e,t)=>{let{children:n,className:s}=e,u=(0,a._T)(e,["children","className"]),{selectedValue:d}=(0,l.useContext)(i.Z),p=d===(0,l.useContext)(r.Z);return l.createElement("div",Object.assign({ref:t,className:(0,o.q)(c("root"),"w-full mt-2",p?"":"hidden",s),"aria-selected":p?"true":"false"},u),n)});u.displayName="TabPanel"},77991:function(e,t,n){"use strict";n.d(t,{Z:function(){return d}});var a=n(5853),r=n(31492);n(42698);var i=n(64016);n(8710);var o=n(33232),s=n(97324),l=n(1153),c=n(2265);let u=(0,l.fn)("TabPanels"),d=c.forwardRef((e,t)=>{let{children:n,className:l}=e,d=(0,a._T)(e,["children","className"]);return c.createElement(r.O.Panels,Object.assign({as:"div",ref:t,className:(0,s.q)(u("root"),"w-full",l)},d),e=>{let{selectedIndex:t}=e;return c.createElement(o.Z.Provider,{value:{selectedValue:t}},c.Children.map(n,(e,t)=>c.createElement(i.Z.Provider,{value:t},e)))})});d.displayName="TabPanels"},42698:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var a=n(2265),r=n(7084);n(97324);let i=(0,a.createContext)(r.fr.Blue)},64016:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)(0)},8710:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)(void 0)},33232:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});let a=(0,n(2265).createContext)({selectedValue:void 0,handleValueChange:void 0})},93942:function(e,t,n){"use strict";n.d(t,{i:function(){return s}});var a=n(2265),r=n(50506),i=n(13959),o=n(71744);function s(e){return t=>a.createElement(i.ZP,{theme:{token:{motion:!1,zIndexPopupBase:0}}},a.createElement(e,Object.assign({},t)))}t.Z=(e,t,n,i)=>s(s=>{let{prefixCls:l,style:c}=s,u=a.useRef(null),[d,p]=a.useState(0),[g,m]=a.useState(0),[b,f]=(0,r.Z)(!1,{value:s.open}),{getPrefixCls:E}=a.useContext(o.E_),h=E(t||"select",l);a.useEffect(()=>{if(f(!0),"undefined"!=typeof ResizeObserver){let e=new ResizeObserver(e=>{let t=e[0].target;p(t.offsetHeight+8),m(t.offsetWidth)}),t=setInterval(()=>{var a;let r=n?".".concat(n(h)):".".concat(h,"-dropdown"),i=null===(a=u.current)||void 0===a?void 0:a.querySelector(r);i&&(clearInterval(t),e.observe(i))},10);return()=>{clearInterval(t),e.disconnect()}}},[]);let S=Object.assign(Object.assign({},s),{style:Object.assign(Object.assign({},c),{margin:0}),open:b,visible:b,getPopupContainer:()=>u.current});return i&&(S=i(S)),a.createElement("div",{ref:u,style:{paddingBottom:d,position:"relative",minWidth:g}},a.createElement(e,Object.assign({},S)))})},53445:function(e,t,n){"use strict";var a=n(2265),r=n(49638);t.Z=function(e,t,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:a.createElement(r.Z,null),o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if("boolean"==typeof e?!e:void 0===t?!o:!1===t||null===t)return[!1,null];let s="boolean"==typeof t||null==t?i:t;return[!0,n?n(s):s]}},91679:function(e,t,n){"use strict";let a;n.d(t,{Z:function(){return eY}});var r=n(83145),i=n(2265),o=n(18404),s=n(71744),l=n(13959),c=n(8900),u=n(39725),d=n(54537),p=n(55726),g=n(36760),m=n.n(g),b=n(62236),f=n(68710),E=n(55274),h=n(29961),S=n(69819),y=n(73002),T=n(51248),A=e=>{let{type:t,children:n,prefixCls:a,buttonProps:r,close:o,autoFocus:s,emitEvent:l,isSilent:c,quitOnNullishReturnValue:u,actionFn:d}=e,p=i.useRef(!1),g=i.useRef(null),[m,b]=(0,S.Z)(!1),f=function(){null==o||o.apply(void 0,arguments)};i.useEffect(()=>{let e=null;return s&&(e=setTimeout(()=>{var e;null===(e=g.current)||void 0===e||e.focus()})),()=>{e&&clearTimeout(e)}},[]);let E=e=>{e&&e.then&&(b(!0),e.then(function(){b(!1,!0),f.apply(void 0,arguments),p.current=!1},e=>{if(b(!1,!0),p.current=!1,null==c||!c())return Promise.reject(e)}))};return i.createElement(y.ZP,Object.assign({},(0,T.nx)(t),{onClick:e=>{let t;if(!p.current){if(p.current=!0,!d){f();return}if(l){var n;if(t=d(e),u&&!((n=t)&&n.then)){p.current=!1,f(e);return}}else if(d.length)t=d(o),p.current=!1;else if(!(t=d())){f();return}E(t)}},loading:m,prefixCls:a},r,{ref:g}),n)};let R=i.createContext({}),{Provider:I}=R;var N=()=>{let{autoFocusButton:e,cancelButtonProps:t,cancelTextLocale:n,isSilent:a,mergedOkCancel:r,rootPrefixCls:o,close:s,onCancel:l,onConfirm:c}=(0,i.useContext)(R);return r?i.createElement(A,{isSilent:a,actionFn:l,close:function(){null==s||s.apply(void 0,arguments),null==c||c(!1)},autoFocus:"cancel"===e,buttonProps:t,prefixCls:"".concat(o,"-btn")},n):null},_=()=>{let{autoFocusButton:e,close:t,isSilent:n,okButtonProps:a,rootPrefixCls:r,okTextLocale:o,okType:s,onConfirm:l,onOk:c}=(0,i.useContext)(R);return i.createElement(A,{isSilent:n,type:s||"primary",actionFn:c,close:function(){null==t||t.apply(void 0,arguments),null==l||l(!0)},autoFocus:"ok"===e,buttonProps:a,prefixCls:"".concat(r,"-btn")},o)},v=n(49638),w=n(1119),k=n(26365),C=n(28036),O=i.createContext({}),x=n(31686),L=n(2161),D=n(92491),P=n(95814),M=n(18242);function F(e,t,n){var a=t;return!a&&n&&(a="".concat(e,"-").concat(n)),a}function U(e,t){var n=e["page".concat(t?"Y":"X","Offset")],a="scroll".concat(t?"Top":"Left");if("number"!=typeof n){var r=e.document;"number"!=typeof(n=r.documentElement[a])&&(n=r.body[a])}return n}var B=n(47970),G=n(28791),$=i.memo(function(e){return e.children},function(e,t){return!t.shouldUpdate}),H={width:0,height:0,overflow:"hidden",outline:"none"},z=i.forwardRef(function(e,t){var n,a,r,o=e.prefixCls,s=e.className,l=e.style,c=e.title,u=e.ariaId,d=e.footer,p=e.closable,g=e.closeIcon,b=e.onClose,f=e.children,E=e.bodyStyle,h=e.bodyProps,S=e.modalRender,y=e.onMouseDown,T=e.onMouseUp,A=e.holderRef,R=e.visible,I=e.forceRender,N=e.width,_=e.height,v=e.classNames,k=e.styles,C=i.useContext(O).panel,L=(0,G.x1)(A,C),D=(0,i.useRef)(),P=(0,i.useRef)();i.useImperativeHandle(t,function(){return{focus:function(){var e;null===(e=D.current)||void 0===e||e.focus()},changeActive:function(e){var t=document.activeElement;e&&t===P.current?D.current.focus():e||t!==D.current||P.current.focus()}}});var M={};void 0!==N&&(M.width=N),void 0!==_&&(M.height=_),d&&(n=i.createElement("div",{className:m()("".concat(o,"-footer"),null==v?void 0:v.footer),style:(0,x.Z)({},null==k?void 0:k.footer)},d)),c&&(a=i.createElement("div",{className:m()("".concat(o,"-header"),null==v?void 0:v.header),style:(0,x.Z)({},null==k?void 0:k.header)},i.createElement("div",{className:"".concat(o,"-title"),id:u},c))),p&&(r=i.createElement("button",{type:"button",onClick:b,"aria-label":"Close",className:"".concat(o,"-close")},g||i.createElement("span",{className:"".concat(o,"-close-x")})));var F=i.createElement("div",{className:m()("".concat(o,"-content"),null==v?void 0:v.content),style:null==k?void 0:k.content},r,a,i.createElement("div",(0,w.Z)({className:m()("".concat(o,"-body"),null==v?void 0:v.body),style:(0,x.Z)((0,x.Z)({},E),null==k?void 0:k.body)},h),f),n);return i.createElement("div",{key:"dialog-element",role:"dialog","aria-labelledby":c?u:null,"aria-modal":"true",ref:L,style:(0,x.Z)((0,x.Z)({},l),M),className:m()(o,s),onMouseDown:y,onMouseUp:T},i.createElement("div",{tabIndex:0,ref:D,style:H,"aria-hidden":"true"}),i.createElement($,{shouldUpdate:R||I},S?S(F):F),i.createElement("div",{tabIndex:0,ref:P,style:H,"aria-hidden":"true"}))}),j=i.forwardRef(function(e,t){var n=e.prefixCls,a=e.title,r=e.style,o=e.className,s=e.visible,l=e.forceRender,c=e.destroyOnClose,u=e.motionName,d=e.ariaId,p=e.onVisibleChanged,g=e.mousePosition,b=(0,i.useRef)(),f=i.useState(),E=(0,k.Z)(f,2),h=E[0],S=E[1],y={};function T(){var e,t,n,a,r,i=(n={left:(t=(e=b.current).getBoundingClientRect()).left,top:t.top},r=(a=e.ownerDocument).defaultView||a.parentWindow,n.left+=U(r),n.top+=U(r,!0),n);S(g?"".concat(g.x-i.left,"px ").concat(g.y-i.top,"px"):"")}return h&&(y.transformOrigin=h),i.createElement(B.ZP,{visible:s,onVisibleChanged:p,onAppearPrepare:T,onEnterPrepare:T,forceRender:l,motionName:u,removeOnLeave:c,ref:b},function(s,l){var c=s.className,u=s.style;return i.createElement(z,(0,w.Z)({},e,{ref:t,title:a,ariaId:d,prefixCls:n,holderRef:l,style:(0,x.Z)((0,x.Z)((0,x.Z)({},u),r),y),className:m()(o,c)}))})});function V(e){var t=e.prefixCls,n=e.style,a=e.visible,r=e.maskProps,o=e.motionName,s=e.className;return i.createElement(B.ZP,{key:"mask",visible:a,motionName:o,leavedClassName:"".concat(t,"-mask-hidden")},function(e,a){var o=e.className,l=e.style;return i.createElement("div",(0,w.Z)({ref:a,style:(0,x.Z)((0,x.Z)({},l),n),className:m()("".concat(t,"-mask"),o,s)},r))})}function W(e){var t=e.prefixCls,n=void 0===t?"rc-dialog":t,a=e.zIndex,r=e.visible,o=void 0!==r&&r,s=e.keyboard,l=void 0===s||s,c=e.focusTriggerAfterClose,u=void 0===c||c,d=e.wrapStyle,p=e.wrapClassName,g=e.wrapProps,b=e.onClose,f=e.afterOpenChange,E=e.afterClose,h=e.transitionName,S=e.animation,y=e.closable,T=e.mask,A=void 0===T||T,R=e.maskTransitionName,I=e.maskAnimation,N=e.maskClosable,_=e.maskStyle,v=e.maskProps,C=e.rootClassName,O=e.classNames,U=e.styles,B=(0,i.useRef)(),G=(0,i.useRef)(),$=(0,i.useRef)(),H=i.useState(o),z=(0,k.Z)(H,2),W=z[0],q=z[1],Y=(0,D.Z)();function K(e){null==b||b(e)}var Z=(0,i.useRef)(!1),X=(0,i.useRef)(),Q=null;return(void 0===N||N)&&(Q=function(e){Z.current?Z.current=!1:G.current===e.target&&K(e)}),(0,i.useEffect)(function(){o&&(q(!0),(0,L.Z)(G.current,document.activeElement)||(B.current=document.activeElement))},[o]),(0,i.useEffect)(function(){return function(){clearTimeout(X.current)}},[]),i.createElement("div",(0,w.Z)({className:m()("".concat(n,"-root"),C)},(0,M.Z)(e,{data:!0})),i.createElement(V,{prefixCls:n,visible:A&&o,motionName:F(n,R,I),style:(0,x.Z)((0,x.Z)({zIndex:a},_),null==U?void 0:U.mask),maskProps:v,className:null==O?void 0:O.mask}),i.createElement("div",(0,w.Z)({tabIndex:-1,onKeyDown:function(e){if(l&&e.keyCode===P.Z.ESC){e.stopPropagation(),K(e);return}o&&e.keyCode===P.Z.TAB&&$.current.changeActive(!e.shiftKey)},className:m()("".concat(n,"-wrap"),p,null==O?void 0:O.wrapper),ref:G,onClick:Q,style:(0,x.Z)((0,x.Z)((0,x.Z)({zIndex:a},d),null==U?void 0:U.wrapper),{},{display:W?null:"none"})},g),i.createElement(j,(0,w.Z)({},e,{onMouseDown:function(){clearTimeout(X.current),Z.current=!0},onMouseUp:function(){X.current=setTimeout(function(){Z.current=!1})},ref:$,closable:void 0===y||y,ariaId:Y,prefixCls:n,visible:o&&W,onClose:K,onVisibleChanged:function(e){if(e)!function(){if(!(0,L.Z)(G.current,document.activeElement)){var e;null===(e=$.current)||void 0===e||e.focus()}}();else{if(q(!1),A&&B.current&&u){try{B.current.focus({preventScroll:!0})}catch(e){}B.current=null}W&&(null==E||E())}null==f||f(e)},motionName:F(n,h,S)}))))}j.displayName="Content",n(32559);var q=function(e){var t=e.visible,n=e.getContainer,a=e.forceRender,r=e.destroyOnClose,o=void 0!==r&&r,s=e.afterClose,l=e.panelRef,c=i.useState(t),u=(0,k.Z)(c,2),d=u[0],p=u[1],g=i.useMemo(function(){return{panel:l}},[l]);return(i.useEffect(function(){t&&p(!0)},[t]),a||!o||d)?i.createElement(O.Provider,{value:g},i.createElement(C.Z,{open:t||a||d,autoDestroy:!1,getContainer:n,autoLock:t||d},i.createElement(W,(0,w.Z)({},e,{destroyOnClose:o,afterClose:function(){null==s||s(),p(!1)}})))):null};q.displayName="Dialog";var Y=n(53445),K=n(94981),Z=n(95140),X=n(39109),Q=n(65658),J=n(74126);function ee(){}let et=i.createContext({add:ee,remove:ee});var en=n(86586),ea=()=>{let{cancelButtonProps:e,cancelTextLocale:t,onCancel:n}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({onClick:n},e),t)},er=()=>{let{confirmLoading:e,okButtonProps:t,okType:n,okTextLocale:a,onOk:r}=(0,i.useContext)(R);return i.createElement(y.ZP,Object.assign({},(0,T.nx)(n),{loading:e,onClick:r},t),a)},ei=n(92246);function eo(e,t){return i.createElement("span",{className:"".concat(e,"-close-x")},t||i.createElement(v.Z,{className:"".concat(e,"-close-icon")}))}let es=e=>{let t;let{okText:n,okType:a="primary",cancelText:o,confirmLoading:s,onOk:l,onCancel:c,okButtonProps:u,cancelButtonProps:d,footer:p}=e,[g]=(0,E.Z)("Modal",(0,ei.A)()),m={confirmLoading:s,okButtonProps:u,cancelButtonProps:d,okTextLocale:n||(null==g?void 0:g.okText),cancelTextLocale:o||(null==g?void 0:g.cancelText),okType:a,onOk:l,onCancel:c},b=i.useMemo(()=>m,(0,r.Z)(Object.values(m)));return"function"==typeof p||void 0===p?(t=i.createElement(i.Fragment,null,i.createElement(ea,null),i.createElement(er,null)),"function"==typeof p&&(t=p(t,{OkBtn:er,CancelBtn:ea})),t=i.createElement(I,{value:b},t)):t=p,i.createElement(en.n,{disabled:!1},t)};var el=n(12918),ec=n(11699),eu=n(691),ed=n(3104),ep=n(80669),eg=n(352);function em(e){return{position:e,inset:0}}let eb=e=>{let{componentCls:t,antCls:n}=e;return[{["".concat(t,"-root")]:{["".concat(t).concat(n,"-zoom-enter, ").concat(t).concat(n,"-zoom-appear")]:{transform:"none",opacity:0,animationDuration:e.motionDurationSlow,userSelect:"none"},["".concat(t).concat(n,"-zoom-leave ").concat(t,"-content")]:{pointerEvents:"none"},["".concat(t,"-mask")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,height:"100%",backgroundColor:e.colorBgMask,pointerEvents:"none",["".concat(t,"-hidden")]:{display:"none"}}),["".concat(t,"-wrap")]:Object.assign(Object.assign({},em("fixed")),{zIndex:e.zIndexPopupBase,overflow:"auto",outline:0,WebkitOverflowScrolling:"touch",["&:has(".concat(t).concat(n,"-zoom-enter), &:has(").concat(t).concat(n,"-zoom-appear)")]:{pointerEvents:"none"}})}},{["".concat(t,"-root")]:(0,ec.J$)(e)}]},ef=e=>{let{componentCls:t}=e;return[{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl"},["".concat(t,"-centered")]:{textAlign:"center","&::before":{display:"inline-block",width:0,height:"100%",verticalAlign:"middle",content:'""'},[t]:{top:0,display:"inline-block",paddingBottom:0,textAlign:"start",verticalAlign:"middle"}},["@media (max-width: ".concat(e.screenSMMax,"px)")]:{[t]:{maxWidth:"calc(100vw - 16px)",margin:"".concat((0,eg.bf)(e.marginXS)," auto")},["".concat(t,"-centered")]:{[t]:{flex:1}}}}},{[t]:Object.assign(Object.assign({},(0,el.Wf)(e)),{pointerEvents:"none",position:"relative",top:100,width:"auto",maxWidth:"calc(100vw - ".concat((0,eg.bf)(e.calc(e.margin).mul(2).equal()),")"),margin:"0 auto",paddingBottom:e.paddingLG,["".concat(t,"-title")]:{margin:0,color:e.titleColor,fontWeight:e.fontWeightStrong,fontSize:e.titleFontSize,lineHeight:e.titleLineHeight,wordWrap:"break-word"},["".concat(t,"-content")]:{position:"relative",backgroundColor:e.contentBg,backgroundClip:"padding-box",border:0,borderRadius:e.borderRadiusLG,boxShadow:e.boxShadow,pointerEvents:"auto",padding:e.contentPadding},["".concat(t,"-close")]:Object.assign({position:"absolute",top:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),insetInlineEnd:e.calc(e.modalHeaderHeight).sub(e.modalCloseBtnSize).div(2).equal(),zIndex:e.calc(e.zIndexPopupBase).add(10).equal(),padding:0,color:e.modalCloseIconColor,fontWeight:e.fontWeightStrong,lineHeight:1,textDecoration:"none",background:"transparent",borderRadius:e.borderRadiusSM,width:e.modalCloseBtnSize,height:e.modalCloseBtnSize,border:0,outline:0,cursor:"pointer",transition:"color ".concat(e.motionDurationMid,", background-color ").concat(e.motionDurationMid),"&-x":{display:"flex",fontSize:e.fontSizeLG,fontStyle:"normal",lineHeight:"".concat((0,eg.bf)(e.modalCloseBtnSize)),justifyContent:"center",textTransform:"none",textRendering:"auto"},"&:hover":{color:e.modalIconHoverColor,backgroundColor:e.closeBtnHoverBg,textDecoration:"none"},"&:active":{backgroundColor:e.closeBtnActiveBg}},(0,el.Qy)(e)),["".concat(t,"-header")]:{color:e.colorText,background:e.headerBg,borderRadius:"".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)," 0 0"),marginBottom:e.headerMarginBottom,padding:e.headerPadding,borderBottom:e.headerBorderBottom},["".concat(t,"-body")]:{fontSize:e.fontSize,lineHeight:e.lineHeight,wordWrap:"break-word",padding:e.bodyPadding},["".concat(t,"-footer")]:{textAlign:"end",background:e.footerBg,marginTop:e.footerMarginTop,padding:e.footerPadding,borderTop:e.footerBorderTop,borderRadius:e.footerBorderRadius,["> ".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginInlineStart:e.marginXS}},["".concat(t,"-open")]:{overflow:"hidden"}})},{["".concat(t,"-pure-panel")]:{top:"auto",padding:0,display:"flex",flexDirection:"column",["".concat(t,"-content,\n ").concat(t,"-body,\n ").concat(t,"-confirm-body-wrapper")]:{display:"flex",flexDirection:"column",flex:"auto"},["".concat(t,"-confirm-body")]:{marginBottom:"auto"}}}]},eE=e=>{let{componentCls:t}=e;return{["".concat(t,"-root")]:{["".concat(t,"-wrap-rtl")]:{direction:"rtl",["".concat(t,"-confirm-body")]:{direction:"rtl"}}}}},eh=e=>{let t=e.padding,n=e.fontSizeHeading5,a=e.lineHeightHeading5;return(0,ed.TS)(e,{modalHeaderHeight:e.calc(e.calc(a).mul(n).equal()).add(e.calc(t).mul(2).equal()).equal(),modalFooterBorderColorSplit:e.colorSplit,modalFooterBorderStyle:e.lineType,modalFooterBorderWidth:e.lineWidth,modalIconHoverColor:e.colorIconHover,modalCloseIconColor:e.colorIcon,modalCloseBtnSize:e.fontHeight,modalConfirmIconSize:e.fontHeight,modalTitleHeight:e.calc(e.titleFontSize).mul(e.titleLineHeight).equal()})},eS=e=>({footerBg:"transparent",headerBg:e.colorBgElevated,titleLineHeight:e.lineHeightHeading5,titleFontSize:e.fontSizeHeading5,contentBg:e.colorBgElevated,titleColor:e.colorTextHeading,closeBtnHoverBg:e.wireframe?"transparent":e.colorFillContent,closeBtnActiveBg:e.wireframe?"transparent":e.colorFillContentHover,contentPadding:e.wireframe?0:"".concat((0,eg.bf)(e.paddingMD)," ").concat((0,eg.bf)(e.paddingContentHorizontalLG)),headerPadding:e.wireframe?"".concat((0,eg.bf)(e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,headerBorderBottom:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",headerMarginBottom:e.wireframe?0:e.marginXS,bodyPadding:e.wireframe?e.paddingLG:0,footerPadding:e.wireframe?"".concat((0,eg.bf)(e.paddingXS)," ").concat((0,eg.bf)(e.padding)):0,footerBorderTop:e.wireframe?"".concat((0,eg.bf)(e.lineWidth)," ").concat(e.lineType," ").concat(e.colorSplit):"none",footerBorderRadius:e.wireframe?"0 0 ".concat((0,eg.bf)(e.borderRadiusLG)," ").concat((0,eg.bf)(e.borderRadiusLG)):0,footerMarginTop:e.wireframe?0:e.marginSM,confirmBodyPadding:e.wireframe?"".concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(2*e.padding)," ").concat((0,eg.bf)(e.paddingLG)):0,confirmIconMarginInlineEnd:e.wireframe?e.margin:e.marginSM,confirmBtnsMarginTop:e.wireframe?e.marginLG:e.marginSM});var ey=(0,ep.I$)("Modal",e=>{let t=eh(e);return[ef(t),eE(t),eb(t),(0,eu._y)(t,"zoom")]},eS,{unitless:{titleLineHeight:!0}}),eT=n(64024),eA=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};(0,K.Z)()&&window.document.documentElement&&document.documentElement.addEventListener("click",e=>{a={x:e.pageX,y:e.pageY},setTimeout(()=>{a=null},100)},!0);var eR=e=>{var t;let{getPopupContainer:n,getPrefixCls:r,direction:o,modal:l}=i.useContext(s.E_),c=t=>{let{onCancel:n}=e;null==n||n(t)},{prefixCls:u,className:d,rootClassName:p,open:g,wrapClassName:E,centered:h,getContainer:S,closeIcon:y,closable:T,focusTriggerAfterClose:A=!0,style:R,visible:I,width:N=520,footer:_,classNames:w,styles:k}=e,C=eA(e,["prefixCls","className","rootClassName","open","wrapClassName","centered","getContainer","closeIcon","closable","focusTriggerAfterClose","style","visible","width","footer","classNames","styles"]),O=r("modal",u),x=r(),L=(0,eT.Z)(O),[D,P,M]=ey(O,L),F=m()(E,{["".concat(O,"-centered")]:!!h,["".concat(O,"-wrap-rtl")]:"rtl"===o}),U=null!==_&&i.createElement(es,Object.assign({},e,{onOk:t=>{let{onOk:n}=e;null==n||n(t)},onCancel:c})),[B,G]=(0,Y.Z)(T,y,e=>eo(O,e),i.createElement(v.Z,{className:"".concat(O,"-close-icon")}),!0),$=function(e){let t=i.useContext(et),n=i.useRef();return(0,J.zX)(a=>{if(a){let r=e?a.querySelector(e):a;t.add(r),n.current=r}else t.remove(n.current)})}(".".concat(O,"-content")),[H,z]=(0,b.Cn)("Modal",C.zIndex);return D(i.createElement(Q.BR,null,i.createElement(X.Ux,{status:!0,override:!0},i.createElement(Z.Z.Provider,{value:z},i.createElement(q,Object.assign({width:N},C,{zIndex:H,getContainer:void 0===S?n:S,prefixCls:O,rootClassName:m()(P,p,M,L),footer:U,visible:null!=g?g:I,mousePosition:null!==(t=C.mousePosition)&&void 0!==t?t:a,onClose:c,closable:B,closeIcon:G,focusTriggerAfterClose:A,transitionName:(0,f.m)(x,"zoom",e.transitionName),maskTransitionName:(0,f.m)(x,"fade",e.maskTransitionName),className:m()(P,d,null==l?void 0:l.className),style:Object.assign(Object.assign({},null==l?void 0:l.style),R),classNames:Object.assign(Object.assign({wrapper:F},null==l?void 0:l.classNames),w),styles:Object.assign(Object.assign({},null==l?void 0:l.styles),k),panelRef:$}))))))};let eI=e=>{let{componentCls:t,titleFontSize:n,titleLineHeight:a,modalConfirmIconSize:r,fontSize:i,lineHeight:o,modalTitleHeight:s,fontHeight:l,confirmBodyPadding:c}=e,u="".concat(t,"-confirm");return{[u]:{"&-rtl":{direction:"rtl"},["".concat(e.antCls,"-modal-header")]:{display:"none"},["".concat(u,"-body-wrapper")]:Object.assign({},(0,el.dF)()),["&".concat(t," ").concat(t,"-body")]:{padding:c},["".concat(u,"-body")]:{display:"flex",flexWrap:"nowrap",alignItems:"start",["> ".concat(e.iconCls)]:{flex:"none",fontSize:r,marginInlineEnd:e.confirmIconMarginInlineEnd,marginTop:e.calc(e.calc(l).sub(r).equal()).div(2).equal()},["&-has-title > ".concat(e.iconCls)]:{marginTop:e.calc(e.calc(s).sub(r).equal()).div(2).equal()}},["".concat(u,"-paragraph")]:{display:"flex",flexDirection:"column",flex:"auto",rowGap:e.marginXS,maxWidth:"calc(100% - ".concat((0,eg.bf)(e.calc(e.modalConfirmIconSize).add(e.marginSM).equal()),")")},["".concat(u,"-title")]:{color:e.colorTextHeading,fontWeight:e.fontWeightStrong,fontSize:n,lineHeight:a},["".concat(u,"-content")]:{color:e.colorText,fontSize:i,lineHeight:o},["".concat(u,"-btns")]:{textAlign:"end",marginTop:e.confirmBtnsMarginTop,["".concat(e.antCls,"-btn + ").concat(e.antCls,"-btn")]:{marginBottom:0,marginInlineStart:e.marginXS}}},["".concat(u,"-error ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorError},["".concat(u,"-warning ").concat(u,"-body > ").concat(e.iconCls,",\n ").concat(u,"-confirm ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorWarning},["".concat(u,"-info ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorInfo},["".concat(u,"-success ").concat(u,"-body > ").concat(e.iconCls)]:{color:e.colorSuccess}}};var eN=(0,ep.bk)(["Modal","confirm"],e=>[eI(eh(e))],eS,{order:-1e3}),e_=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n};function ev(e){let{prefixCls:t,icon:n,okText:a,cancelText:o,confirmPrefixCls:s,type:l,okCancel:g,footer:b,locale:f}=e,h=e_(e,["prefixCls","icon","okText","cancelText","confirmPrefixCls","type","okCancel","footer","locale"]),S=n;if(!n&&null!==n)switch(l){case"info":S=i.createElement(p.Z,null);break;case"success":S=i.createElement(c.Z,null);break;case"error":S=i.createElement(u.Z,null);break;default:S=i.createElement(d.Z,null)}let y=null!=g?g:"confirm"===l,T=null!==e.autoFocusButton&&(e.autoFocusButton||"ok"),[A]=(0,E.Z)("Modal"),R=f||A,v=a||(y?null==R?void 0:R.okText:null==R?void 0:R.justOkText),w=Object.assign({autoFocusButton:T,cancelTextLocale:o||(null==R?void 0:R.cancelText),okTextLocale:v,mergedOkCancel:y},h),k=i.useMemo(()=>w,(0,r.Z)(Object.values(w))),C=i.createElement(i.Fragment,null,i.createElement(N,null),i.createElement(_,null)),O=void 0!==e.title&&null!==e.title,x="".concat(s,"-body");return i.createElement("div",{className:"".concat(s,"-body-wrapper")},i.createElement("div",{className:m()(x,{["".concat(x,"-has-title")]:O})},S,i.createElement("div",{className:"".concat(s,"-paragraph")},O&&i.createElement("span",{className:"".concat(s,"-title")},e.title),i.createElement("div",{className:"".concat(s,"-content")},e.content))),void 0===b||"function"==typeof b?i.createElement(I,{value:k},i.createElement("div",{className:"".concat(s,"-btns")},"function"==typeof b?b(C,{OkBtn:_,CancelBtn:N}):C)):b,i.createElement(eN,{prefixCls:t}))}let ew=e=>{let{close:t,zIndex:n,afterClose:a,open:r,keyboard:o,centered:s,getContainer:l,maskStyle:c,direction:u,prefixCls:d,wrapClassName:p,rootPrefixCls:g,bodyStyle:E,closable:S=!1,closeIcon:y,modalRender:T,focusTriggerAfterClose:A,onConfirm:R,styles:I}=e,N="".concat(d,"-confirm"),_=e.width||416,v=e.style||{},w=void 0===e.mask||e.mask,k=void 0!==e.maskClosable&&e.maskClosable,C=m()(N,"".concat(N,"-").concat(e.type),{["".concat(N,"-rtl")]:"rtl"===u},e.className),[,O]=(0,h.ZP)(),x=i.useMemo(()=>void 0!==n?n:O.zIndexPopupBase+b.u6,[n,O]);return i.createElement(eR,{prefixCls:d,className:C,wrapClassName:m()({["".concat(N,"-centered")]:!!e.centered},p),onCancel:()=>{null==t||t({triggerCancel:!0}),null==R||R(!1)},open:r,title:"",footer:null,transitionName:(0,f.m)(g||"","zoom",e.transitionName),maskTransitionName:(0,f.m)(g||"","fade",e.maskTransitionName),mask:w,maskClosable:k,style:v,styles:Object.assign({body:E,mask:c},I),width:_,zIndex:x,afterClose:a,keyboard:o,centered:s,getContainer:l,closable:S,closeIcon:y,modalRender:T,focusTriggerAfterClose:A},i.createElement(ev,Object.assign({},e,{confirmPrefixCls:N})))};var ek=e=>{let{rootPrefixCls:t,iconPrefixCls:n,direction:a,theme:r}=e;return i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:n,direction:a,theme:r},i.createElement(ew,Object.assign({},e)))},eC=[];let eO="",ex=e=>{var t,n;let{prefixCls:a,getContainer:r,direction:o}=e,l=(0,ei.A)(),c=(0,i.useContext)(s.E_),u=eO||c.getPrefixCls(),d=a||"".concat(u,"-modal"),p=r;return!1===p&&(p=void 0),i.createElement(ek,Object.assign({},e,{rootPrefixCls:u,prefixCls:d,iconPrefixCls:c.iconPrefixCls,theme:c.theme,direction:null!=o?o:c.direction,locale:null!==(n=null===(t=c.locale)||void 0===t?void 0:t.Modal)&&void 0!==n?n:l,getContainer:p}))};function eL(e){let t;let n=(0,l.w6)(),a=document.createDocumentFragment(),s=Object.assign(Object.assign({},e),{close:d,open:!0});function c(){for(var t=arguments.length,n=Array(t),i=0;ie&&e.triggerCancel);e.onCancel&&s&&e.onCancel.apply(e,[()=>{}].concat((0,r.Z)(n.slice(1))));for(let e=0;e{let t=n.getPrefixCls(void 0,eO),r=n.getIconPrefixCls(),s=n.getTheme(),c=i.createElement(ex,Object.assign({},e));(0,o.s)(i.createElement(l.ZP,{prefixCls:t,iconPrefixCls:r,theme:s},n.holderRender?n.holderRender(c):c),a)})}function d(){for(var t=arguments.length,n=Array(t),a=0;a{"function"==typeof e.afterClose&&e.afterClose(),c.apply(this,n)}})).visible&&delete s.visible,u(s)}return u(s),eC.push(d),{destroy:d,update:function(e){u(s="function"==typeof e?e(s):Object.assign(Object.assign({},s),e))}}}function eD(e){return Object.assign(Object.assign({},e),{type:"warning"})}function eP(e){return Object.assign(Object.assign({},e),{type:"info"})}function eM(e){return Object.assign(Object.assign({},e),{type:"success"})}function eF(e){return Object.assign(Object.assign({},e),{type:"error"})}function eU(e){return Object.assign(Object.assign({},e),{type:"confirm"})}var eB=n(93942),eG=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},e$=(0,eB.i)(e=>{let{prefixCls:t,className:n,closeIcon:a,closable:r,type:o,title:l,children:c,footer:u}=e,d=eG(e,["prefixCls","className","closeIcon","closable","type","title","children","footer"]),{getPrefixCls:p}=i.useContext(s.E_),g=p(),b=t||p("modal"),f=(0,eT.Z)(g),[E,h,S]=ey(b,f),y="".concat(b,"-confirm"),T={};return T=o?{closable:null!=r&&r,title:"",footer:"",children:i.createElement(ev,Object.assign({},e,{prefixCls:b,confirmPrefixCls:y,rootPrefixCls:g,content:c}))}:{closable:null==r||r,title:l,footer:null!==u&&i.createElement(es,Object.assign({},e)),children:c},E(i.createElement(z,Object.assign({prefixCls:b,className:m()(h,"".concat(b,"-pure-panel"),o&&y,o&&"".concat(y,"-").concat(o),n,S,f)},d,{closeIcon:eo(b,a),closable:r},T)))}),eH=n(13823),ez=function(e,t){var n={};for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&0>t.indexOf(a)&&(n[a]=e[a]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols)for(var r=0,a=Object.getOwnPropertySymbols(e);rt.indexOf(a[r])&&Object.prototype.propertyIsEnumerable.call(e,a[r])&&(n[a[r]]=e[a[r]]);return n},ej=i.forwardRef((e,t)=>{var n,{afterClose:a,config:o}=e,l=ez(e,["afterClose","config"]);let[c,u]=i.useState(!0),[d,p]=i.useState(o),{direction:g,getPrefixCls:m}=i.useContext(s.E_),b=m("modal"),f=m(),h=function(){u(!1);for(var e=arguments.length,t=Array(e),n=0;ne&&e.triggerCancel);d.onCancel&&a&&d.onCancel.apply(d,[()=>{}].concat((0,r.Z)(t.slice(1))))};i.useImperativeHandle(t,()=>({destroy:h,update:e=>{p(t=>Object.assign(Object.assign({},t),e))}}));let S=null!==(n=d.okCancel)&&void 0!==n?n:"confirm"===d.type,[y]=(0,E.Z)("Modal",eH.Z.Modal);return i.createElement(ek,Object.assign({prefixCls:b,rootPrefixCls:f},d,{close:h,open:c,afterClose:()=>{var e;a(),null===(e=d.afterClose)||void 0===e||e.call(d)},okText:d.okText||(S?null==y?void 0:y.okText:null==y?void 0:y.justOkText),direction:d.direction||g,cancelText:d.cancelText||(null==y?void 0:y.cancelText)},l))});let eV=0,eW=i.memo(i.forwardRef((e,t)=>{let[n,a]=function(){let[e,t]=i.useState([]);return[e,i.useCallback(e=>(t(t=>[].concat((0,r.Z)(t),[e])),()=>{t(t=>t.filter(t=>t!==e))}),[])]}();return i.useImperativeHandle(t,()=>({patchElement:a}),[]),i.createElement(i.Fragment,null,n)}));function eq(e){return eL(eD(e))}eR.useModal=function(){let e=i.useRef(null),[t,n]=i.useState([]);i.useEffect(()=>{t.length&&((0,r.Z)(t).forEach(e=>{e()}),n([]))},[t]);let a=i.useCallback(t=>function(a){var o;let s,l;eV+=1;let c=i.createRef(),u=new Promise(e=>{s=e}),d=!1,p=i.createElement(ej,{key:"modal-".concat(eV),config:t(a),ref:c,afterClose:()=>{null==l||l()},isSilent:()=>d,onConfirm:e=>{s(e)}});return(l=null===(o=e.current)||void 0===o?void 0:o.patchElement(p))&&eC.push(l),{destroy:()=>{function e(){var e;null===(e=c.current)||void 0===e||e.destroy()}c.current?e():n(t=>[].concat((0,r.Z)(t),[e]))},update:e=>{function t(){var t;null===(t=c.current)||void 0===t||t.update(e)}c.current?t():n(e=>[].concat((0,r.Z)(e),[t]))},then:e=>(d=!0,u.then(e))}},[]);return[i.useMemo(()=>({info:a(eP),success:a(eM),error:a(eF),warning:a(eD),confirm:a(eU)}),[]),i.createElement(eW,{key:"modal-holder",ref:e})]},eR.info=function(e){return eL(eP(e))},eR.success=function(e){return eL(eM(e))},eR.error=function(e){return eL(eF(e))},eR.warning=eq,eR.warn=eq,eR.confirm=function(e){return eL(eU(e))},eR.destroyAll=function(){for(;eC.length;){let e=eC.pop();e&&e()}},eR.config=function(e){let{rootPrefixCls:t}=e;eO=t},eR._InternalPanelDoNotUseOrYouWillBeFired=e$;var eY=eR},11699:function(e,t,n){"use strict";n.d(t,{J$:function(){return s}});var a=n(352),r=n(37133);let i=new a.E4("antFadeIn",{"0%":{opacity:0},"100%":{opacity:1}}),o=new a.E4("antFadeOut",{"0%":{opacity:1},"100%":{opacity:0}}),s=function(e){let t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],{antCls:n}=e,a="".concat(n,"-fade"),s=t?"&":"";return[(0,r.R)(a,i,o,e.motionDurationMid,t),{["\n ".concat(s).concat(a,"-enter,\n ").concat(s).concat(a,"-appear\n ")]:{opacity:0,animationTimingFunction:"linear"},["".concat(s).concat(a,"-leave")]:{animationTimingFunction:"linear"}}]}},26035:function(e){"use strict";e.exports=function(e,n){for(var a,r,i,o=e||"",s=n||"div",l={},c=0;c4&&m.slice(0,4)===o&&s.test(t)&&("-"===t.charAt(4)?b=o+(n=t.slice(5).replace(l,d)).charAt(0).toUpperCase()+n.slice(1):(g=(p=t).slice(4),t=l.test(g)?p:("-"!==(g=g.replace(c,u)).charAt(0)&&(g="-"+g),o+g)),f=r),new f(b,t))};var s=/^data[-\w.:]+$/i,l=/-[a-z]/g,c=/[A-Z]/g;function u(e){return"-"+e.toLowerCase()}function d(e){return e.charAt(1).toUpperCase()}},30466:function(e,t,n){"use strict";var a=n(82855),r=n(64541),i=n(80808),o=n(44987),s=n(72731),l=n(98946);e.exports=a([i,r,o,s,l])},72731:function(e,t,n){"use strict";var a=n(20321),r=n(41757),i=a.booleanish,o=a.number,s=a.spaceSeparated;e.exports=r({transform:function(e,t){return"role"===t?t:"aria-"+t.slice(4).toLowerCase()},properties:{ariaActiveDescendant:null,ariaAtomic:i,ariaAutoComplete:null,ariaBusy:i,ariaChecked:i,ariaColCount:o,ariaColIndex:o,ariaColSpan:o,ariaControls:s,ariaCurrent:null,ariaDescribedBy:s,ariaDetails:null,ariaDisabled:i,ariaDropEffect:s,ariaErrorMessage:null,ariaExpanded:i,ariaFlowTo:s,ariaGrabbed:i,ariaHasPopup:null,ariaHidden:i,ariaInvalid:null,ariaKeyShortcuts:null,ariaLabel:null,ariaLabelledBy:s,ariaLevel:o,ariaLive:null,ariaModal:i,ariaMultiLine:i,ariaMultiSelectable:i,ariaOrientation:null,ariaOwns:s,ariaPlaceholder:null,ariaPosInSet:o,ariaPressed:i,ariaReadOnly:i,ariaRelevant:null,ariaRequired:i,ariaRoleDescription:s,ariaRowCount:o,ariaRowIndex:o,ariaRowSpan:o,ariaSelected:i,ariaSetSize:o,ariaSort:null,ariaValueMax:o,ariaValueMin:o,ariaValueNow:o,ariaValueText:null,role:null}})},98946:function(e,t,n){"use strict";var a=n(20321),r=n(41757),i=n(53296),o=a.boolean,s=a.overloadedBoolean,l=a.booleanish,c=a.number,u=a.spaceSeparated,d=a.commaSeparated;e.exports=r({space:"html",attributes:{acceptcharset:"accept-charset",classname:"class",htmlfor:"for",httpequiv:"http-equiv"},transform:i,mustUseProperty:["checked","multiple","muted","selected"],properties:{abbr:null,accept:d,acceptCharset:u,accessKey:u,action:null,allow:null,allowFullScreen:o,allowPaymentRequest:o,allowUserMedia:o,alt:null,as:null,async:o,autoCapitalize:null,autoComplete:u,autoFocus:o,autoPlay:o,capture:o,charSet:null,checked:o,cite:null,className:u,cols:c,colSpan:null,content:null,contentEditable:l,controls:o,controlsList:u,coords:c|d,crossOrigin:null,data:null,dateTime:null,decoding:null,default:o,defer:o,dir:null,dirName:null,disabled:o,download:s,draggable:l,encType:null,enterKeyHint:null,form:null,formAction:null,formEncType:null,formMethod:null,formNoValidate:o,formTarget:null,headers:u,height:c,hidden:o,high:c,href:null,hrefLang:null,htmlFor:u,httpEquiv:u,id:null,imageSizes:null,imageSrcSet:d,inputMode:null,integrity:null,is:null,isMap:o,itemId:null,itemProp:u,itemRef:u,itemScope:o,itemType:u,kind:null,label:null,lang:null,language:null,list:null,loading:null,loop:o,low:c,manifest:null,max:null,maxLength:c,media:null,method:null,min:null,minLength:c,multiple:o,muted:o,name:null,nonce:null,noModule:o,noValidate:o,onAbort:null,onAfterPrint:null,onAuxClick:null,onBeforePrint:null,onBeforeUnload:null,onBlur:null,onCancel:null,onCanPlay:null,onCanPlayThrough:null,onChange:null,onClick:null,onClose:null,onContextMenu:null,onCopy:null,onCueChange:null,onCut:null,onDblClick:null,onDrag:null,onDragEnd:null,onDragEnter:null,onDragExit:null,onDragLeave:null,onDragOver:null,onDragStart:null,onDrop:null,onDurationChange:null,onEmptied:null,onEnded:null,onError:null,onFocus:null,onFormData:null,onHashChange:null,onInput:null,onInvalid:null,onKeyDown:null,onKeyPress:null,onKeyUp:null,onLanguageChange:null,onLoad:null,onLoadedData:null,onLoadedMetadata:null,onLoadEnd:null,onLoadStart:null,onMessage:null,onMessageError:null,onMouseDown:null,onMouseEnter:null,onMouseLeave:null,onMouseMove:null,onMouseOut:null,onMouseOver:null,onMouseUp:null,onOffline:null,onOnline:null,onPageHide:null,onPageShow:null,onPaste:null,onPause:null,onPlay:null,onPlaying:null,onPopState:null,onProgress:null,onRateChange:null,onRejectionHandled:null,onReset:null,onResize:null,onScroll:null,onSecurityPolicyViolation:null,onSeeked:null,onSeeking:null,onSelect:null,onSlotChange:null,onStalled:null,onStorage:null,onSubmit:null,onSuspend:null,onTimeUpdate:null,onToggle:null,onUnhandledRejection:null,onUnload:null,onVolumeChange:null,onWaiting:null,onWheel:null,open:o,optimum:c,pattern:null,ping:u,placeholder:null,playsInline:o,poster:null,preload:null,readOnly:o,referrerPolicy:null,rel:u,required:o,reversed:o,rows:c,rowSpan:c,sandbox:u,scope:null,scoped:o,seamless:o,selected:o,shape:null,size:c,sizes:null,slot:null,span:c,spellCheck:l,src:null,srcDoc:null,srcLang:null,srcSet:d,start:c,step:null,style:null,tabIndex:c,target:null,title:null,translate:null,type:null,typeMustMatch:o,useMap:null,value:l,width:c,wrap:null,align:null,aLink:null,archive:u,axis:null,background:null,bgColor:null,border:c,borderColor:null,bottomMargin:c,cellPadding:null,cellSpacing:null,char:null,charOff:null,classId:null,clear:null,code:null,codeBase:null,codeType:null,color:null,compact:o,declare:o,event:null,face:null,frame:null,frameBorder:null,hSpace:c,leftMargin:c,link:null,longDesc:null,lowSrc:null,marginHeight:c,marginWidth:c,noResize:o,noHref:o,noShade:o,noWrap:o,object:null,profile:null,prompt:null,rev:null,rightMargin:c,rules:null,scheme:null,scrolling:l,standby:null,summary:null,text:null,topMargin:c,valueType:null,version:null,vAlign:null,vLink:null,vSpace:c,allowTransparency:null,autoCorrect:null,autoSave:null,disablePictureInPicture:o,disableRemotePlayback:o,prefix:null,property:null,results:c,security:null,unselectable:null}})},53296:function(e,t,n){"use strict";var a=n(38781);e.exports=function(e,t){return a(e,t.toLowerCase())}},38781:function(e){"use strict";e.exports=function(e,t){return t in e?e[t]:t}},41757:function(e,t,n){"use strict";var a=n(96532),r=n(61723),i=n(51351);e.exports=function(e){var t,n,o=e.space,s=e.mustUseProperty||[],l=e.attributes||{},c=e.properties,u=e.transform,d={},p={};for(t in c)n=new i(t,u(l,t),c[t],o),-1!==s.indexOf(t)&&(n.mustUseProperty=!0),d[t]=n,p[a(t)]=t,p[a(n.attribute)]=t;return new r(d,p,o)}},51351:function(e,t,n){"use strict";var a=n(24192),r=n(20321);e.exports=s,s.prototype=new a,s.prototype.defined=!0;var i=["boolean","booleanish","overloadedBoolean","number","commaSeparated","spaceSeparated","commaOrSpaceSeparated"],o=i.length;function s(e,t,n,s){var l,c,u,d=-1;for(s&&(this.space=s),a.call(this,e,t);++d=u.reach));A+=T.value.length,T=T.next){var R,I=T.value;if(n.length>t.length)return;if(!(I instanceof i)){var N=1;if(E){if(!(R=o(y,A,t,f))||R.index>=t.length)break;var _=R.index,v=R.index+R[0].length,w=A;for(w+=T.value.length;_>=w;)w+=(T=T.next).value.length;if(w-=T.value.length,A=w,T.value instanceof i)continue;for(var k=T;k!==n.tail&&(wu.reach&&(u.reach=L);var D=T.prev;if(O&&(D=l(n,D,O),A+=O.length),function(e,t,n){for(var a=t.next,r=0;r1){var P={cause:d+","+g,reach:L};e(t,n,a,T.prev,A,P),u&&P.reach>u.reach&&(u.reach=P.reach)}}}}}}(e,c,t,c.head,0),function(e){for(var t=[],n=e.head.next;n!==e.tail;)t.push(n.value),n=n.next;return t}(c)},hooks:{all:{},add:function(e,t){var n=r.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=r.hooks.all[e];if(n&&n.length)for(var a,i=0;a=n[i++];)a(t)}},Token:i};function i(e,t,n,a){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length}function o(e,t,n,a){e.lastIndex=t;var r=e.exec(n);if(r&&a&&r[1]){var i=r[1].length;r.index+=i,r[0]=r[0].slice(i)}return r}function s(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function l(e,t,n){var a=t.next,r={value:n,prev:t,next:a};return t.next=r,a.prev=r,e.length++,r}if(e.Prism=r,i.stringify=function e(t,n){if("string"==typeof t)return t;if(Array.isArray(t)){var a="";return t.forEach(function(t){a+=e(t,n)}),a}var i={type:t.type,content:e(t.content,n),tag:"span",classes:["token",t.type],attributes:{},language:n},o=t.alias;o&&(Array.isArray(o)?Array.prototype.push.apply(i.classes,o):i.classes.push(o)),r.hooks.run("wrap",i);var s="";for(var l in i.attributes)s+=" "+l+'="'+(i.attributes[l]||"").replace(/"/g,""")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+s+">"+i.content+""+i.tag+">"},!e.document)return e.addEventListener&&(r.disableWorkerMessageHandler||e.addEventListener("message",function(t){var n=JSON.parse(t.data),a=n.language,i=n.code,o=n.immediateClose;e.postMessage(r.highlight(i,r.languages[a],a)),o&&e.close()},!1)),r;var c=r.util.currentScript();function u(){r.manual||r.highlightAll()}if(c&&(r.filename=c.src,c.hasAttribute("data-manual")&&(r.manual=!0)),!r.manual){var d=document.readyState;"loading"===d||"interactive"===d&&c&&c.defer?document.addEventListener("DOMContentLoaded",u):window.requestAnimationFrame?window.requestAnimationFrame(u):window.setTimeout(u,16)}return r}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=a),void 0!==n.g&&(n.g.Prism=a)},18242:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var a=n(31686),r="".concat("accept acceptCharset accessKey action allowFullScreen allowTransparency\n alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge\n charSet checked classID className colSpan cols content contentEditable contextMenu\n controls coords crossOrigin data dateTime default defer dir disabled download draggable\n encType form formAction formEncType formMethod formNoValidate formTarget frameBorder\n headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity\n is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media\n mediaGroup method min minLength multiple muted name noValidate nonce open\n optimum pattern placeholder poster preload radioGroup readOnly rel required\n reversed role rowSpan rows sandbox scope scoped scrolling seamless selected\n shape size sizes span spellCheck src srcDoc srcLang srcSet start step style\n summary tabIndex target title type useMap value width wmode wrap"," ").concat("onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown\n onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick\n onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown\n onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel\n onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough\n onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata\n onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError").split(/[\s\n]+/);function i(e,t){return 0===e.indexOf(t)}function o(e){var t,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];t=!1===n?{aria:!0,data:!0,attr:!0}:!0===n?{aria:!0}:(0,a.Z)({},n);var o={};return Object.keys(e).forEach(function(n){(t.aria&&("role"===n||i(n,"aria-"))||t.data&&i(n,"data-")||t.attr&&r.includes(n))&&(o[n]=e[n])}),o}},17906:function(e,t,n){"use strict";n.d(t,{Z:function(){return N}});var a,r,i=n(6989),o=n(83145),s=n(11993),l=n(2265),c=n(1119);function u(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,a)}return n}function d(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return(function(e){if(0===e.length||1===e.length)return e;var t,n=e.join(".");return p[n]||(p[n]=0===(t=e.length)||1===t?e:2===t?[e[0],e[1],"".concat(e[0],".").concat(e[1]),"".concat(e[1],".").concat(e[0])]:3===t?[e[0],e[1],e[2],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0])]:t>=4?[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[3],".").concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[2],".").concat(e[1],".").concat(e[0])]:void 0),p[n]})(e.filter(function(e){return"token"!==e})).reduce(function(e,t){return d(d({},e),n[t])},t)}(s.className,Object.assign({},s.style,void 0===r?{}:r),a)})}else f=d(d({},s),{},{className:s.className.join(" ")});var T=E(n.children);return l.createElement(g,(0,c.Z)({key:o},f),T)}}({node:e,stylesheet:n,useInlineStyles:a,key:"code-segement".concat(t)})})}function A(e){return e&&void 0!==e.highlightAuto}var R=n(99113),I=(a=n.n(R)(),r={'code[class*="language-"]':{color:"black",background:"none",textShadow:"0 1px white",fontFamily:"Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",fontSize:"1em",textAlign:"left",whiteSpace:"pre",wordSpacing:"normal",wordBreak:"normal",wordWrap:"normal",lineHeight:"1.5",MozTabSize:"4",OTabSize:"4",tabSize:"4",WebkitHyphens:"none",MozHyphens:"none",msHyphens:"none",hyphens:"none"},'pre[class*="language-"]':{color:"black",background:"#f5f2f0",textShadow:"0 1px white",fontFamily:"Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",fontSize:"1em",textAlign:"left",whiteSpace:"pre",wordSpacing:"normal",wordBreak:"normal",wordWrap:"normal",lineHeight:"1.5",MozTabSize:"4",OTabSize:"4",tabSize:"4",WebkitHyphens:"none",MozHyphens:"none",msHyphens:"none",hyphens:"none",padding:"1em",margin:".5em 0",overflow:"auto"},'pre[class*="language-"]::-moz-selection':{textShadow:"none",background:"#b3d4fc"},'pre[class*="language-"] ::-moz-selection':{textShadow:"none",background:"#b3d4fc"},'code[class*="language-"]::-moz-selection':{textShadow:"none",background:"#b3d4fc"},'code[class*="language-"] ::-moz-selection':{textShadow:"none",background:"#b3d4fc"},'pre[class*="language-"]::selection':{textShadow:"none",background:"#b3d4fc"},'pre[class*="language-"] ::selection':{textShadow:"none",background:"#b3d4fc"},'code[class*="language-"]::selection':{textShadow:"none",background:"#b3d4fc"},'code[class*="language-"] ::selection':{textShadow:"none",background:"#b3d4fc"},':not(pre) > code[class*="language-"]':{background:"#f5f2f0",padding:".1em",borderRadius:".3em",whiteSpace:"normal"},comment:{color:"slategray"},prolog:{color:"slategray"},doctype:{color:"slategray"},cdata:{color:"slategray"},punctuation:{color:"#999"},namespace:{Opacity:".7"},property:{color:"#905"},tag:{color:"#905"},boolean:{color:"#905"},number:{color:"#905"},constant:{color:"#905"},symbol:{color:"#905"},deleted:{color:"#905"},selector:{color:"#690"},"attr-name":{color:"#690"},string:{color:"#690"},char:{color:"#690"},builtin:{color:"#690"},inserted:{color:"#690"},operator:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},entity:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)",cursor:"help"},url:{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".language-css .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},".style .token.string":{color:"#9a6e3a",background:"hsla(0, 0%, 100%, .5)"},atrule:{color:"#07a"},"attr-value":{color:"#07a"},keyword:{color:"#07a"},function:{color:"#DD4A68"},"class-name":{color:"#DD4A68"},regex:{color:"#e90"},important:{color:"#e90",fontWeight:"bold"},variable:{color:"#e90"},bold:{fontWeight:"bold"},italic:{fontStyle:"italic"}},function(e){var t=e.language,n=e.children,s=e.style,c=void 0===s?r:s,u=e.customStyle,d=void 0===u?{}:u,p=e.codeTagProps,m=void 0===p?{className:t?"language-".concat(t):void 0,style:b(b({},c['code[class*="language-"]']),c['code[class*="language-'.concat(t,'"]')])}:p,R=e.useInlineStyles,I=void 0===R||R,N=e.showLineNumbers,_=void 0!==N&&N,v=e.showInlineLineNumbers,w=void 0===v||v,k=e.startingLineNumber,C=void 0===k?1:k,O=e.lineNumberContainerStyle,x=e.lineNumberStyle,L=void 0===x?{}:x,D=e.wrapLines,P=e.wrapLongLines,M=void 0!==P&&P,F=e.lineProps,U=e.renderer,B=e.PreTag,G=void 0===B?"pre":B,$=e.CodeTag,H=void 0===$?"code":$,z=e.code,j=void 0===z?(Array.isArray(n)?n[0]:n)||"":z,V=e.astGenerator,W=(0,i.Z)(e,g);V=V||a;var q=_?l.createElement(E,{containerStyle:O,codeStyle:m.style||{},numberStyle:L,startingLineNumber:C,codeString:j}):null,Y=c.hljs||c['pre[class*="language-"]']||{backgroundColor:"#fff"},K=A(V)?"hljs":"prismjs",Z=I?Object.assign({},W,{style:Object.assign({},Y,d)}):Object.assign({},W,{className:W.className?"".concat(K," ").concat(W.className):K,style:Object.assign({},d)});if(M?m.style=b({whiteSpace:"pre-wrap"},m.style):m.style=b({whiteSpace:"pre"},m.style),!V)return l.createElement(G,Z,q,l.createElement(H,m,j));(void 0===D&&U||M)&&(D=!0),U=U||T;var X=[{type:"text",value:j}],Q=function(e){var t=e.astGenerator,n=e.language,a=e.code,r=e.defaultCodeValue;if(A(t)){var i=-1!==t.listLanguages().indexOf(n);return"text"===n?{value:r,language:"text"}:i?t.highlight(n,a):t.highlightAuto(a)}try{return n&&"text"!==n?{value:t.highlight(a,n)}:{value:r}}catch(e){return{value:r}}}({astGenerator:V,language:t,code:j,defaultCodeValue:X});null===Q.language&&(Q.value=X);var J=Q.value.length;1===J&&"text"===Q.value[0].type&&(J=Q.value[0].value.split("\n").length);var ee=J+C,et=function(e,t,n,a,r,i,s,l,c){var u,d=function e(t){for(var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return t||o.length>0?function(e,i){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];return y({children:e,lineNumber:i,lineNumberStyle:l,largestLineNumber:s,showInlineLineNumbers:r,lineProps:n,className:o,showLineNumbers:a,wrapLongLines:c,wrapLines:t})}(e,i,o):function(e,t){if(a&&t&&r){var n=S(l,t,s);e.unshift(h(t,n))}return e}(e,i)}for(;m]?|>=?|\?=|[-+\/=])(?=\s)/,lookbehind:!0},"string-operator":{pattern:/(\s)&&?(?=\s)/,lookbehind:!0,alias:"keyword"},"token-operator":[{pattern:/(\w)(?:->?|=>|[~|{}])(?=\w)/,lookbehind:!0,alias:"punctuation"},{pattern:/[|{}]/,alias:"punctuation"}],punctuation:/[,.:()]/}}e.exports=t,t.displayName="abap",t.aliases=[]},36463:function(e){"use strict";function t(e){var t;t="(?:ALPHA|BIT|CHAR|CR|CRLF|CTL|DIGIT|DQUOTE|HEXDIG|HTAB|LF|LWSP|OCTET|SP|VCHAR|WSP)",e.languages.abnf={comment:/;.*/,string:{pattern:/(?:%[is])?"[^"\n\r]*"/,greedy:!0,inside:{punctuation:/^%[is]/}},range:{pattern:/%(?:b[01]+-[01]+|d\d+-\d+|x[A-F\d]+-[A-F\d]+)/i,alias:"number"},terminal:{pattern:/%(?:b[01]+(?:\.[01]+)*|d\d+(?:\.\d+)*|x[A-F\d]+(?:\.[A-F\d]+)*)/i,alias:"number"},repetition:{pattern:/(^|[^\w-])(?:\d*\*\d*|\d+)/,lookbehind:!0,alias:"operator"},definition:{pattern:/(^[ \t]*)(?:[a-z][\w-]*|<[^<>\r\n]*>)(?=\s*=)/m,lookbehind:!0,alias:"keyword",inside:{punctuation:/<|>/}},"core-rule":{pattern:RegExp("(?:(^|[^<\\w-])"+t+"|<"+t+">)(?![\\w-])","i"),lookbehind:!0,alias:["rule","constant"],inside:{punctuation:/<|>/}},rule:{pattern:/(^|[^<\w-])[a-z][\w-]*|<[^<>\r\n]*>/i,lookbehind:!0,inside:{punctuation:/<|>/}},operator:/=\/?|\//,punctuation:/[()\[\]]/}}e.exports=t,t.displayName="abnf",t.aliases=[]},25276:function(e){"use strict";function t(e){e.languages.actionscript=e.languages.extend("javascript",{keyword:/\b(?:as|break|case|catch|class|const|default|delete|do|dynamic|each|else|extends|final|finally|for|function|get|if|implements|import|in|include|instanceof|interface|internal|is|namespace|native|new|null|override|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|use|var|void|while|with)\b/,operator:/\+\+|--|(?:[+\-*\/%^]|&&?|\|\|?|<|>>?>?|[!=]=?)=?|[~?@]/}),e.languages.actionscript["class-name"].alias="function",delete e.languages.actionscript.parameter,delete e.languages.actionscript["literal-property"],e.languages.markup&&e.languages.insertBefore("actionscript","string",{xml:{pattern:/(^|[^.])<\/?\w+(?:\s+[^\s>\/=]+=("|')(?:\\[\s\S]|(?!\2)[^\\])*\2)*\s*\/?>/,lookbehind:!0,inside:e.languages.markup}})}e.exports=t,t.displayName="actionscript",t.aliases=[]},82679:function(e){"use strict";function t(e){e.languages.ada={comment:/--.*/,string:/"(?:""|[^"\r\f\n])*"/,number:[{pattern:/\b\d(?:_?\d)*#[\dA-F](?:_?[\dA-F])*(?:\.[\dA-F](?:_?[\dA-F])*)?#(?:E[+-]?\d(?:_?\d)*)?/i},{pattern:/\b\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:E[+-]?\d(?:_?\d)*)?\b/i}],"attr-name":/\b'\w+/,keyword:/\b(?:abort|abs|abstract|accept|access|aliased|all|and|array|at|begin|body|case|constant|declare|delay|delta|digits|do|else|elsif|end|entry|exception|exit|for|function|generic|goto|if|in|interface|is|limited|loop|mod|new|not|null|of|others|out|overriding|package|pragma|private|procedure|protected|raise|range|record|rem|renames|requeue|return|reverse|select|separate|some|subtype|synchronized|tagged|task|terminate|then|type|until|use|when|while|with|xor)\b/i,boolean:/\b(?:false|true)\b/i,operator:/<[=>]?|>=?|=>?|:=|\/=?|\*\*?|[&+-]/,punctuation:/\.\.?|[,;():]/,char:/'.'/,variable:/\b[a-z](?:\w)*\b/i}}e.exports=t,t.displayName="ada",t.aliases=[]},80943:function(e){"use strict";function t(e){e.languages.agda={comment:/\{-[\s\S]*?(?:-\}|$)|--.*/,string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^\\\r\n"])*"/,greedy:!0},punctuation:/[(){}⦃⦄.;@]/,"class-name":{pattern:/((?:data|record) +)\S+/,lookbehind:!0},function:{pattern:/(^[ \t]*)(?!\s)[^:\r\n]+(?=:)/m,lookbehind:!0},operator:{pattern:/(^\s*|\s)(?:[=|:∀→λ\\?_]|->)(?=\s)/,lookbehind:!0},keyword:/\b(?:Set|abstract|constructor|data|eta-equality|field|forall|hiding|import|in|inductive|infix|infixl|infixr|instance|let|macro|module|mutual|no-eta-equality|open|overlap|pattern|postulate|primitive|private|public|quote|quoteContext|quoteGoal|quoteTerm|record|renaming|rewrite|syntax|tactic|unquote|unquoteDecl|unquoteDef|using|variable|where|with)\b/}}e.exports=t,t.displayName="agda",t.aliases=[]},34168:function(e){"use strict";function t(e){e.languages.al={comment:/\/\/.*|\/\*[\s\S]*?\*\//,string:{pattern:/'(?:''|[^'\r\n])*'(?!')|"(?:""|[^"\r\n])*"(?!")/,greedy:!0},function:{pattern:/(\b(?:event|procedure|trigger)\s+|(?:^|[^.])\.\s*)[a-z_]\w*(?=\s*\()/i,lookbehind:!0},keyword:[/\b(?:array|asserterror|begin|break|case|do|downto|else|end|event|exit|for|foreach|function|if|implements|in|indataset|interface|internal|local|of|procedure|program|protected|repeat|runonclient|securityfiltering|suppressdispose|temporary|then|to|trigger|until|var|while|with|withevents)\b/i,/\b(?:action|actions|addafter|addbefore|addfirst|addlast|area|assembly|chartpart|codeunit|column|controladdin|cuegroup|customizes|dataitem|dataset|dotnet|elements|enum|enumextension|extends|field|fieldattribute|fieldelement|fieldgroup|fieldgroups|fields|filter|fixed|grid|group|key|keys|label|labels|layout|modify|moveafter|movebefore|movefirst|movelast|page|pagecustomization|pageextension|part|profile|query|repeater|report|requestpage|schema|separator|systempart|table|tableelement|tableextension|textattribute|textelement|type|usercontrol|value|xmlport)\b/i],number:/\b(?:0x[\da-f]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?)(?:F|LL?|U(?:LL?)?)?\b/i,boolean:/\b(?:false|true)\b/i,variable:/\b(?:Curr(?:FieldNo|Page|Report)|x?Rec|RequestOptionsPage)\b/,"class-name":/\b(?:automation|biginteger|bigtext|blob|boolean|byte|char|clienttype|code|completiontriggererrorlevel|connectiontype|database|dataclassification|datascope|date|dateformula|datetime|decimal|defaultlayout|dialog|dictionary|dotnetassembly|dotnettypedeclaration|duration|errorinfo|errortype|executioncontext|executionmode|fieldclass|fieldref|fieldtype|file|filterpagebuilder|guid|httpclient|httpcontent|httpheaders|httprequestmessage|httpresponsemessage|instream|integer|joker|jsonarray|jsonobject|jsontoken|jsonvalue|keyref|list|moduledependencyinfo|moduleinfo|none|notification|notificationscope|objecttype|option|outstream|pageresult|record|recordid|recordref|reportformat|securityfilter|sessionsettings|tableconnectiontype|tablefilter|testaction|testfield|testfilterfield|testpage|testpermissions|testrequestpage|text|textbuilder|textconst|textencoding|time|transactionmodel|transactiontype|variant|verbosity|version|view|views|webserviceactioncontext|webserviceactionresultcode|xmlattribute|xmlattributecollection|xmlcdata|xmlcomment|xmldeclaration|xmldocument|xmldocumenttype|xmlelement|xmlnamespacemanager|xmlnametable|xmlnode|xmlnodelist|xmlprocessinginstruction|xmlreadoptions|xmltext|xmlwriteoptions)\b/i,operator:/\.\.|:[=:]|[-+*/]=?|<>|[<>]=?|=|\b(?:and|div|mod|not|or|xor)\b/i,punctuation:/[()\[\]{}:.;,]/}}e.exports=t,t.displayName="al",t.aliases=[]},25262:function(e){"use strict";function t(e){e.languages.antlr4={comment:/\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,string:{pattern:/'(?:\\.|[^\\'\r\n])*'/,greedy:!0},"character-class":{pattern:/\[(?:\\.|[^\\\]\r\n])*\]/,greedy:!0,alias:"regex",inside:{range:{pattern:/([^[]|(?:^|[^\\])(?:\\\\)*\\\[)-(?!\])/,lookbehind:!0,alias:"punctuation"},escape:/\\(?:u(?:[a-fA-F\d]{4}|\{[a-fA-F\d]+\})|[pP]\{[=\w-]+\}|[^\r\nupP])/,punctuation:/[\[\]]/}},action:{pattern:/\{(?:[^{}]|\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})*\}/,greedy:!0,inside:{content:{pattern:/(\{)[\s\S]+(?=\})/,lookbehind:!0},punctuation:/[{}]/}},command:{pattern:/(->\s*(?!\s))(?:\s*(?:,\s*)?\b[a-z]\w*(?:\s*\([^()\r\n]*\))?)+(?=\s*;)/i,lookbehind:!0,inside:{function:/\b\w+(?=\s*(?:[,(]|$))/,punctuation:/[,()]/}},annotation:{pattern:/@\w+(?:::\w+)*/,alias:"keyword"},label:{pattern:/#[ \t]*\w+/,alias:"punctuation"},keyword:/\b(?:catch|channels|finally|fragment|grammar|import|lexer|locals|mode|options|parser|returns|throws|tokens)\b/,definition:[{pattern:/\b[a-z]\w*(?=\s*:)/,alias:["rule","class-name"]},{pattern:/\b[A-Z]\w*(?=\s*:)/,alias:["token","constant"]}],constant:/\b[A-Z][A-Z_]*\b/,operator:/\.\.|->|[|~]|[*+?]\??/,punctuation:/[;:()=]/},e.languages.g4=e.languages.antlr4}e.exports=t,t.displayName="antlr4",t.aliases=["g4"]},60486:function(e){"use strict";function t(e){e.languages.apacheconf={comment:/#.*/,"directive-inline":{pattern:/(^[\t ]*)\b(?:AcceptFilter|AcceptPathInfo|AccessFileName|Action|Add(?:Alt|AltByEncoding|AltByType|Charset|DefaultCharset|Description|Encoding|Handler|Icon|IconByEncoding|IconByType|InputFilter|Language|ModuleInfo|OutputFilter|OutputFilterByType|Type)|Alias|AliasMatch|Allow(?:CONNECT|EncodedSlashes|Methods|Override|OverrideList)?|Anonymous(?:_LogEmail|_MustGiveEmail|_NoUserID|_VerifyEmail)?|AsyncRequestWorkerFactor|Auth(?:BasicAuthoritative|BasicFake|BasicProvider|BasicUseDigestAlgorithm|DBDUserPWQuery|DBDUserRealmQuery|DBMGroupFile|DBMType|DBMUserFile|Digest(?:Algorithm|Domain|NonceLifetime|Provider|Qop|ShmemSize)|Form(?:Authoritative|Body|DisableNoStore|FakeBasicAuth|Location|LoginRequiredLocation|LoginSuccessLocation|LogoutLocation|Method|Mimetype|Password|Provider|SitePassphrase|Size|Username)|GroupFile|LDAP(?:AuthorizePrefix|BindAuthoritative|BindDN|BindPassword|CharsetConfig|CompareAsUser|CompareDNOnServer|DereferenceAliases|GroupAttribute|GroupAttributeIsDN|InitialBindAsUser|InitialBindPattern|MaxSubGroupDepth|RemoteUserAttribute|RemoteUserIsDN|SearchAsUser|SubGroupAttribute|SubGroupClass|Url)|Merging|Name|nCache(?:Context|Enable|ProvideFor|SOCache|Timeout)|nzFcgiCheckAuthnProvider|nzFcgiDefineProvider|Type|UserFile|zDBDLoginToReferer|zDBDQuery|zDBDRedirectQuery|zDBMType|zSendForbiddenOnFailure)|BalancerGrowth|BalancerInherit|BalancerMember|BalancerPersist|BrowserMatch|BrowserMatchNoCase|BufferedLogs|BufferSize|Cache(?:DefaultExpire|DetailHeader|DirLength|DirLevels|Disable|Enable|File|Header|IgnoreCacheControl|IgnoreHeaders|IgnoreNoLastMod|IgnoreQueryString|IgnoreURLSessionIdentifiers|KeyBaseURL|LastModifiedFactor|Lock|LockMaxAge|LockPath|MaxExpire|MaxFileSize|MinExpire|MinFileSize|NegotiatedDocs|QuickHandler|ReadSize|ReadTime|Root|Socache(?:MaxSize|MaxTime|MinTime|ReadSize|ReadTime)?|StaleOnError|StoreExpired|StoreNoStore|StorePrivate)|CGIDScriptTimeout|CGIMapExtension|CharsetDefault|CharsetOptions|CharsetSourceEnc|CheckCaseOnly|CheckSpelling|ChrootDir|ContentDigest|CookieDomain|CookieExpires|CookieName|CookieStyle|CookieTracking|CoreDumpDirectory|CustomLog|Dav|DavDepthInfinity|DavGenericLockDB|DavLockDB|DavMinTimeout|DBDExptime|DBDInitSQL|DBDKeep|DBDMax|DBDMin|DBDParams|DBDPersist|DBDPrepareSQL|DBDriver|DefaultIcon|DefaultLanguage|DefaultRuntimeDir|DefaultType|Define|Deflate(?:BufferSize|CompressionLevel|FilterNote|InflateLimitRequestBody|InflateRatio(?:Burst|Limit)|MemLevel|WindowSize)|Deny|DirectoryCheckHandler|DirectoryIndex|DirectoryIndexRedirect|DirectorySlash|DocumentRoot|DTracePrivileges|DumpIOInput|DumpIOOutput|EnableExceptionHook|EnableMMAP|EnableSendfile|Error|ErrorDocument|ErrorLog|ErrorLogFormat|Example|ExpiresActive|ExpiresByType|ExpiresDefault|ExtendedStatus|ExtFilterDefine|ExtFilterOptions|FallbackResource|FileETag|FilterChain|FilterDeclare|FilterProtocol|FilterProvider|FilterTrace|ForceLanguagePriority|ForceType|ForensicLog|GprofDir|GracefulShutdownTimeout|Group|Header|HeaderName|Heartbeat(?:Address|Listen|MaxServers|Storage)|HostnameLookups|IdentityCheck|IdentityCheckTimeout|ImapBase|ImapDefault|ImapMenu|Include|IncludeOptional|Index(?:HeadInsert|Ignore|IgnoreReset|Options|OrderDefault|StyleSheet)|InputSed|ISAPI(?:AppendLogToErrors|AppendLogToQuery|CacheFile|FakeAsync|LogNotSupported|ReadAheadBuffer)|KeepAlive|KeepAliveTimeout|KeptBodySize|LanguagePriority|LDAP(?:CacheEntries|CacheTTL|ConnectionPoolTTL|ConnectionTimeout|LibraryDebug|OpCacheEntries|OpCacheTTL|ReferralHopLimit|Referrals|Retries|RetryDelay|SharedCacheFile|SharedCacheSize|Timeout|TrustedClientCert|TrustedGlobalCert|TrustedMode|VerifyServerCert)|Limit(?:InternalRecursion|Request(?:Body|Fields|FieldSize|Line)|XMLRequestBody)|Listen|ListenBackLog|LoadFile|LoadModule|LogFormat|LogLevel|LogMessage|LuaAuthzProvider|LuaCodeCache|Lua(?:Hook(?:AccessChecker|AuthChecker|CheckUserID|Fixups|InsertFilter|Log|MapToStorage|TranslateName|TypeChecker)|Inherit|InputFilter|MapHandler|OutputFilter|PackageCPath|PackagePath|QuickHandler|Root|Scope)|Max(?:ConnectionsPerChild|KeepAliveRequests|MemFree|RangeOverlaps|RangeReversals|Ranges|RequestWorkers|SpareServers|SpareThreads|Threads)|MergeTrailers|MetaDir|MetaFiles|MetaSuffix|MimeMagicFile|MinSpareServers|MinSpareThreads|MMapFile|ModemStandard|ModMimeUsePathInfo|MultiviewsMatch|Mutex|NameVirtualHost|NoProxy|NWSSLTrustedCerts|NWSSLUpgradeable|Options|Order|OutputSed|PassEnv|PidFile|PrivilegesMode|Protocol|ProtocolEcho|Proxy(?:AddHeaders|BadHeader|Block|Domain|ErrorOverride|ExpressDBMFile|ExpressDBMType|ExpressEnable|FtpDirCharset|FtpEscapeWildcards|FtpListOnWildcard|HTML(?:BufSize|CharsetOut|DocType|Enable|Events|Extended|Fixups|Interp|Links|Meta|StripComments|URLMap)|IOBufferSize|MaxForwards|Pass(?:Inherit|InterpolateEnv|Match|Reverse|ReverseCookieDomain|ReverseCookiePath)?|PreserveHost|ReceiveBufferSize|Remote|RemoteMatch|Requests|SCGIInternalRedirect|SCGISendfile|Set|SourceAddress|Status|Timeout|Via)|ReadmeName|ReceiveBufferSize|Redirect|RedirectMatch|RedirectPermanent|RedirectTemp|ReflectorHeader|RemoteIP(?:Header|InternalProxy|InternalProxyList|ProxiesHeader|TrustedProxy|TrustedProxyList)|RemoveCharset|RemoveEncoding|RemoveHandler|RemoveInputFilter|RemoveLanguage|RemoveOutputFilter|RemoveType|RequestHeader|RequestReadTimeout|Require|Rewrite(?:Base|Cond|Engine|Map|Options|Rule)|RLimitCPU|RLimitMEM|RLimitNPROC|Satisfy|ScoreBoardFile|Script(?:Alias|AliasMatch|InterpreterSource|Log|LogBuffer|LogLength|Sock)?|SecureListen|SeeRequestTail|SendBufferSize|Server(?:Admin|Alias|Limit|Name|Path|Root|Signature|Tokens)|Session(?:Cookie(?:Name|Name2|Remove)|Crypto(?:Cipher|Driver|Passphrase|PassphraseFile)|DBD(?:CookieName|CookieName2|CookieRemove|DeleteLabel|InsertLabel|PerUser|SelectLabel|UpdateLabel)|Env|Exclude|Header|Include|MaxAge)?|SetEnv|SetEnvIf|SetEnvIfExpr|SetEnvIfNoCase|SetHandler|SetInputFilter|SetOutputFilter|SSIEndTag|SSIErrorMsg|SSIETag|SSILastModified|SSILegacyExprParser|SSIStartTag|SSITimeFormat|SSIUndefinedEcho|SSL(?:CACertificateFile|CACertificatePath|CADNRequestFile|CADNRequestPath|CARevocationCheck|CARevocationFile|CARevocationPath|CertificateChainFile|CertificateFile|CertificateKeyFile|CipherSuite|Compression|CryptoDevice|Engine|FIPS|HonorCipherOrder|InsecureRenegotiation|OCSP(?:DefaultResponder|Enable|OverrideResponder|ResponderTimeout|ResponseMaxAge|ResponseTimeSkew|UseRequestNonce)|OpenSSLConfCmd|Options|PassPhraseDialog|Protocol|Proxy(?:CACertificateFile|CACertificatePath|CARevocation(?:Check|File|Path)|CheckPeer(?:CN|Expire|Name)|CipherSuite|Engine|MachineCertificate(?:ChainFile|File|Path)|Protocol|Verify|VerifyDepth)|RandomSeed|RenegBufferSize|Require|RequireSSL|Session(?:Cache|CacheTimeout|TicketKeyFile|Tickets)|SRPUnknownUserSeed|SRPVerifierFile|Stapling(?:Cache|ErrorCacheTimeout|FakeTryLater|ForceURL|ResponderTimeout|ResponseMaxAge|ResponseTimeSkew|ReturnResponderErrors|StandardCacheTimeout)|StrictSNIVHostCheck|UserName|UseStapling|VerifyClient|VerifyDepth)|StartServers|StartThreads|Substitute|Suexec|SuexecUserGroup|ThreadLimit|ThreadsPerChild|ThreadStackSize|TimeOut|TraceEnable|TransferLog|TypesConfig|UnDefine|UndefMacro|UnsetEnv|Use|UseCanonicalName|UseCanonicalPhysicalPort|User|UserDir|VHostCGIMode|VHostCGIPrivs|VHostGroup|VHostPrivs|VHostSecure|VHostUser|Virtual(?:DocumentRoot|ScriptAlias)(?:IP)?|WatchdogInterval|XBitHack|xml2EncAlias|xml2EncDefault|xml2StartParse)\b/im,lookbehind:!0,alias:"property"},"directive-block":{pattern:/<\/?\b(?:Auth[nz]ProviderAlias|Directory|DirectoryMatch|Else|ElseIf|Files|FilesMatch|If|IfDefine|IfModule|IfVersion|Limit|LimitExcept|Location|LocationMatch|Macro|Proxy|Require(?:All|Any|None)|VirtualHost)\b.*>/i,inside:{"directive-block":{pattern:/^<\/?\w+/,inside:{punctuation:/^<\/?/},alias:"tag"},"directive-block-parameter":{pattern:/.*[^>]/,inside:{punctuation:/:/,string:{pattern:/("|').*\1/,inside:{variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/}}},alias:"attr-value"},punctuation:/>/},alias:"tag"},"directive-flags":{pattern:/\[(?:[\w=],?)+\]/,alias:"keyword"},string:{pattern:/("|').*\1/,inside:{variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/}},variable:/[$%]\{?(?:\w\.?[-+:]?)+\}?/,regex:/\^?.*\$|\^.*\$?/}}e.exports=t,t.displayName="apacheconf",t.aliases=[]},5134:function(e,t,n){"use strict";var a=n(12782);function r(e){e.register(a),function(e){var t=/\b(?:(?:after|before)(?=\s+[a-z])|abstract|activate|and|any|array|as|asc|autonomous|begin|bigdecimal|blob|boolean|break|bulk|by|byte|case|cast|catch|char|class|collect|commit|const|continue|currency|date|datetime|decimal|default|delete|desc|do|double|else|end|enum|exception|exit|export|extends|final|finally|float|for|from|get(?=\s*[{};])|global|goto|group|having|hint|if|implements|import|in|inner|insert|instanceof|int|integer|interface|into|join|like|limit|list|long|loop|map|merge|new|not|null|nulls|number|object|of|on|or|outer|override|package|parallel|pragma|private|protected|public|retrieve|return|rollback|select|set|short|sObject|sort|static|string|super|switch|synchronized|system|testmethod|then|this|throw|time|transaction|transient|trigger|try|undelete|update|upsert|using|virtual|void|webservice|when|where|while|(?:inherited|with|without)\s+sharing)\b/i,n=/\b(?:(?=[a-z_]\w*\s*[<\[])|(?!))[A-Z_]\w*(?:\s*\.\s*[A-Z_]\w*)*\b(?:\s*(?:\[\s*\]|<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>))*/.source.replace(//g,function(){return t.source});function a(e){return RegExp(e.replace(//g,function(){return n}),"i")}var r={keyword:t,punctuation:/[()\[\]{};,:.<>]/};e.languages.apex={comment:e.languages.clike.comment,string:e.languages.clike.string,sql:{pattern:/((?:[=,({:]|\breturn)\s*)\[[^\[\]]*\]/i,lookbehind:!0,greedy:!0,alias:"language-sql",inside:e.languages.sql},annotation:{pattern:/@\w+\b/,alias:"punctuation"},"class-name":[{pattern:a(/(\b(?:class|enum|extends|implements|instanceof|interface|new|trigger\s+\w+\s+on)\s+)/.source),lookbehind:!0,inside:r},{pattern:a(/(\(\s*)(?=\s*\)\s*[\w(])/.source),lookbehind:!0,inside:r},{pattern:a(/(?=\s*\w+\s*[;=,(){:])/.source),inside:r}],trigger:{pattern:/(\btrigger\s+)\w+\b/i,lookbehind:!0,alias:"class-name"},keyword:t,function:/\b[a-z_]\w*(?=\s*\()/i,boolean:/\b(?:false|true)\b/i,number:/(?:\B\.\d+|\b\d+(?:\.\d+|L)?)\b/i,operator:/[!=](?:==?)?|\?\.?|&&|\|\||--|\+\+|[-+*/^&|]=?|:|<=?|>{1,3}=?/,punctuation:/[()\[\]{};,.]/}}(e)}e.exports=r,r.displayName="apex",r.aliases=[]},63960:function(e){"use strict";function t(e){e.languages.apl={comment:/(?:⍝|#[! ]).*$/m,string:{pattern:/'(?:[^'\r\n]|'')*'/,greedy:!0},number:/¯?(?:\d*\.?\b\d+(?:e[+¯]?\d+)?|¯|∞)(?:j¯?(?:(?:\d+(?:\.\d+)?|\.\d+)(?:e[+¯]?\d+)?|¯|∞))?/i,statement:/:[A-Z][a-z][A-Za-z]*\b/,"system-function":{pattern:/⎕[A-Z]+/i,alias:"function"},constant:/[⍬⌾#⎕⍞]/,function:/[-+×÷⌈⌊∣|⍳⍸?*⍟○!⌹<≤=>≥≠≡≢∊⍷∪∩~∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⊆⊇⌷⍋⍒⊤⊥⍕⍎⊣⊢⍁⍂≈⍯↗¤→]/,"monadic-operator":{pattern:/[\\\/⌿⍀¨⍨⌶&∥]/,alias:"operator"},"dyadic-operator":{pattern:/[.⍣⍠⍤∘⌸@⌺⍥]/,alias:"operator"},assignment:{pattern:/←/,alias:"keyword"},punctuation:/[\[;\]()◇⋄]/,dfn:{pattern:/[{}⍺⍵⍶⍹∇⍫:]/,alias:"builtin"}}}e.exports=t,t.displayName="apl",t.aliases=[]},51228:function(e){"use strict";function t(e){e.languages.applescript={comment:[/\(\*(?:\(\*(?:[^*]|\*(?!\)))*\*\)|(?!\(\*)[\s\S])*?\*\)/,/--.+/,/#.+/],string:/"(?:\\.|[^"\\\r\n])*"/,number:/(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e-?\d+)?\b/i,operator:[/[&=≠≤≥*+\-\/÷^]|[<>]=?/,/\b(?:(?:begin|end|start)s? with|(?:contains?|(?:does not|doesn't) contain)|(?:is|isn't|is not) (?:contained by|in)|(?:(?:is|isn't|is not) )?(?:greater|less) than(?: or equal)?(?: to)?|(?:comes|(?:does not|doesn't) come) (?:after|before)|(?:is|isn't|is not) equal(?: to)?|(?:(?:does not|doesn't) equal|equal to|equals|is not|isn't)|(?:a )?(?:ref(?: to)?|reference to)|(?:and|as|div|mod|not|or))\b/],keyword:/\b(?:about|above|after|against|apart from|around|aside from|at|back|before|beginning|behind|below|beneath|beside|between|but|by|considering|continue|copy|does|eighth|else|end|equal|error|every|exit|false|fifth|first|for|fourth|from|front|get|given|global|if|ignoring|in|instead of|into|is|it|its|last|local|me|middle|my|ninth|of|on|onto|out of|over|prop|property|put|repeat|return|returning|second|set|seventh|since|sixth|some|tell|tenth|that|the|then|third|through|thru|timeout|times|to|transaction|true|try|until|where|while|whose|with|without)\b/,"class-name":/\b(?:POSIX file|RGB color|alias|application|boolean|centimeters|centimetres|class|constant|cubic centimeters|cubic centimetres|cubic feet|cubic inches|cubic meters|cubic metres|cubic yards|date|degrees Celsius|degrees Fahrenheit|degrees Kelvin|feet|file|gallons|grams|inches|integer|kilograms|kilometers|kilometres|list|liters|litres|meters|metres|miles|number|ounces|pounds|quarts|real|record|reference|script|square feet|square kilometers|square kilometres|square meters|square metres|square miles|square yards|text|yards)\b/,punctuation:/[{}():,¬«»《》]/}}e.exports=t,t.displayName="applescript",t.aliases=[]},29606:function(e){"use strict";function t(e){e.languages.aql={comment:/\/\/.*|\/\*[\s\S]*?\*\//,property:{pattern:/([{,]\s*)(?:(?!\d)\w+|(["'´`])(?:(?!\2)[^\\\r\n]|\\.)*\2)(?=\s*:)/,lookbehind:!0,greedy:!0},string:{pattern:/(["'])(?:(?!\1)[^\\\r\n]|\\.)*\1/,greedy:!0},identifier:{pattern:/([´`])(?:(?!\1)[^\\\r\n]|\\.)*\1/,greedy:!0},variable:/@@?\w+/,keyword:[{pattern:/(\bWITH\s+)COUNT(?=\s+INTO\b)/i,lookbehind:!0},/\b(?:AGGREGATE|ALL|AND|ANY|ASC|COLLECT|DESC|DISTINCT|FILTER|FOR|GRAPH|IN|INBOUND|INSERT|INTO|K_PATHS|K_SHORTEST_PATHS|LET|LIKE|LIMIT|NONE|NOT|NULL|OR|OUTBOUND|REMOVE|REPLACE|RETURN|SHORTEST_PATH|SORT|UPDATE|UPSERT|WINDOW|WITH)\b/i,{pattern:/(^|[^\w.[])(?:KEEP|PRUNE|SEARCH|TO)\b/i,lookbehind:!0},{pattern:/(^|[^\w.[])(?:CURRENT|NEW|OLD)\b/,lookbehind:!0},{pattern:/\bOPTIONS(?=\s*\{)/i}],function:/\b(?!\d)\w+(?=\s*\()/,boolean:/\b(?:false|true)\b/i,range:{pattern:/\.\./,alias:"operator"},number:[/\b0b[01]+/i,/\b0x[0-9a-f]+/i,/(?:\B\.\d+|\b(?:0|[1-9]\d*)(?:\.\d+)?)(?:e[+-]?\d+)?/i],operator:/\*{2,}|[=!]~|[!=<>]=?|&&|\|\||[-+*/%]/,punctuation:/::|[?.:,;()[\]{}]/}}e.exports=t,t.displayName="aql",t.aliases=[]},59760:function(e,t,n){"use strict";var a=n(85028);function r(e){e.register(a),e.languages.arduino=e.languages.extend("cpp",{keyword:/\b(?:String|array|bool|boolean|break|byte|case|catch|continue|default|do|double|else|finally|for|function|goto|if|in|instanceof|int|integer|long|loop|new|null|return|setup|string|switch|throw|try|void|while|word)\b/,constant:/\b(?:ANALOG_MESSAGE|DEFAULT|DIGITAL_MESSAGE|EXTERNAL|FIRMATA_STRING|HIGH|INPUT|INPUT_PULLUP|INTERNAL|INTERNAL1V1|INTERNAL2V56|LED_BUILTIN|LOW|OUTPUT|REPORT_ANALOG|REPORT_DIGITAL|SET_PIN_MODE|SYSEX_START|SYSTEM_RESET)\b/,builtin:/\b(?:Audio|BSSID|Bridge|Client|Console|EEPROM|Esplora|EsploraTFT|Ethernet|EthernetClient|EthernetServer|EthernetUDP|File|FileIO|FileSystem|Firmata|GPRS|GSM|GSMBand|GSMClient|GSMModem|GSMPIN|GSMScanner|GSMServer|GSMVoiceCall|GSM_SMS|HttpClient|IPAddress|IRread|Keyboard|KeyboardController|LiquidCrystal|LiquidCrystal_I2C|Mailbox|Mouse|MouseController|PImage|Process|RSSI|RobotControl|RobotMotor|SD|SPI|SSID|Scheduler|Serial|Server|Servo|SoftwareSerial|Stepper|Stream|TFT|Task|USBHost|WiFi|WiFiClient|WiFiServer|WiFiUDP|Wire|YunClient|YunServer|abs|addParameter|analogRead|analogReadResolution|analogReference|analogWrite|analogWriteResolution|answerCall|attach|attachGPRS|attachInterrupt|attached|autoscroll|available|background|beep|begin|beginPacket|beginSD|beginSMS|beginSpeaker|beginTFT|beginTransmission|beginWrite|bit|bitClear|bitRead|bitSet|bitWrite|blink|blinkVersion|buffer|changePIN|checkPIN|checkPUK|checkReg|circle|cityNameRead|cityNameWrite|clear|clearScreen|click|close|compassRead|config|connect|connected|constrain|cos|countryNameRead|countryNameWrite|createChar|cursor|debugPrint|delay|delayMicroseconds|detach|detachInterrupt|digitalRead|digitalWrite|disconnect|display|displayLogos|drawBMP|drawCompass|encryptionType|end|endPacket|endSMS|endTransmission|endWrite|exists|exitValue|fill|find|findUntil|flush|gatewayIP|get|getAsynchronously|getBand|getButton|getCurrentCarrier|getIMEI|getKey|getModifiers|getOemKey|getPINUsed|getResult|getSignalStrength|getSocket|getVoiceCallStatus|getXChange|getYChange|hangCall|height|highByte|home|image|interrupts|isActionDone|isDirectory|isListening|isPIN|isPressed|isValid|keyPressed|keyReleased|keyboardRead|knobRead|leftToRight|line|lineFollowConfig|listen|listenOnLocalhost|loadImage|localIP|lowByte|macAddress|maintain|map|max|messageAvailable|micros|millis|min|mkdir|motorsStop|motorsWrite|mouseDragged|mouseMoved|mousePressed|mouseReleased|move|noAutoscroll|noBlink|noBuffer|noCursor|noDisplay|noFill|noInterrupts|noListenOnLocalhost|noStroke|noTone|onReceive|onRequest|open|openNextFile|overflow|parseCommand|parseFloat|parseInt|parsePacket|pauseMode|peek|pinMode|playFile|playMelody|point|pointTo|position|pow|prepare|press|print|printFirmwareVersion|printVersion|println|process|processInput|pulseIn|put|random|randomSeed|read|readAccelerometer|readBlue|readButton|readBytes|readBytesUntil|readGreen|readJoystickButton|readJoystickSwitch|readJoystickX|readJoystickY|readLightSensor|readMessage|readMicrophone|readNetworks|readRed|readSlider|readString|readStringUntil|readTemperature|ready|rect|release|releaseAll|remoteIP|remoteNumber|remotePort|remove|requestFrom|retrieveCallingNumber|rewindDirectory|rightToLeft|rmdir|robotNameRead|robotNameWrite|run|runAsynchronously|runShellCommand|runShellCommandAsynchronously|running|scanNetworks|scrollDisplayLeft|scrollDisplayRight|seek|sendAnalog|sendDigitalPortPair|sendDigitalPorts|sendString|sendSysex|serialEvent|setBand|setBitOrder|setClockDivider|setCursor|setDNS|setDataMode|setFirmwareVersion|setMode|setPINUsed|setSpeed|setTextSize|setTimeout|shiftIn|shiftOut|shutdown|sin|size|sqrt|startLoop|step|stop|stroke|subnetMask|switchPIN|tan|tempoWrite|text|tone|transfer|tuneWrite|turn|updateIR|userNameRead|userNameWrite|voiceCall|waitContinue|width|write|writeBlue|writeGreen|writeJSON|writeMessage|writeMicroseconds|writeRGB|writeRed|yield)\b/}),e.languages.ino=e.languages.arduino}e.exports=r,r.displayName="arduino",r.aliases=["ino"]},36023:function(e){"use strict";function t(e){e.languages.arff={comment:/%.*/,string:{pattern:/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},keyword:/@(?:attribute|data|end|relation)\b/i,number:/\b\d+(?:\.\d+)?\b/,punctuation:/[{},]/}}e.exports=t,t.displayName="arff",t.aliases=[]},26894:function(e){"use strict";function t(e){!function(e){var t={pattern:/(^[ \t]*)\[(?!\[)(?:(["'$`])(?:(?!\2)[^\\]|\\.)*\2|\[(?:[^\[\]\\]|\\.)*\]|[^\[\]\\"'$`]|\\.)*\]/m,lookbehind:!0,inside:{quoted:{pattern:/([$`])(?:(?!\1)[^\\]|\\.)*\1/,inside:{punctuation:/^[$`]|[$`]$/}},interpreted:{pattern:/'(?:[^'\\]|\\.)*'/,inside:{punctuation:/^'|'$/}},string:/"(?:[^"\\]|\\.)*"/,variable:/\w+(?==)/,punctuation:/^\[|\]$|,/,operator:/=/,"attr-value":/(?!^\s+$).+/}},n=e.languages.asciidoc={"comment-block":{pattern:/^(\/{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1/m,alias:"comment"},table:{pattern:/^\|={3,}(?:(?:\r?\n|\r(?!\n)).*)*?(?:\r?\n|\r)\|={3,}$/m,inside:{specifiers:{pattern:/(?:(?:(?:\d+(?:\.\d+)?|\.\d+)[+*](?:[<^>](?:\.[<^>])?|\.[<^>])?|[<^>](?:\.[<^>])?|\.[<^>])[a-z]*|[a-z]+)(?=\|)/,alias:"attr-value"},punctuation:{pattern:/(^|[^\\])[|!]=*/,lookbehind:!0}}},"passthrough-block":{pattern:/^(\+{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^\++|\++$/}},"literal-block":{pattern:/^(-{4,}|\.{4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^(?:-+|\.+)|(?:-+|\.+)$/}},"other-block":{pattern:/^(--|\*{4,}|_{4,}|={4,})(?:\r?\n|\r)(?:[\s\S]*(?:\r?\n|\r))??\1$/m,inside:{punctuation:/^(?:-+|\*+|_+|=+)|(?:-+|\*+|_+|=+)$/}},"list-punctuation":{pattern:/(^[ \t]*)(?:-|\*{1,5}|\.{1,5}|(?:[a-z]|\d+)\.|[xvi]+\))(?= )/im,lookbehind:!0,alias:"punctuation"},"list-label":{pattern:/(^[ \t]*)[a-z\d].+(?::{2,4}|;;)(?=\s)/im,lookbehind:!0,alias:"symbol"},"indented-block":{pattern:/((\r?\n|\r)\2)([ \t]+)\S.*(?:(?:\r?\n|\r)\3.+)*(?=\2{2}|$)/,lookbehind:!0},comment:/^\/\/.*/m,title:{pattern:/^.+(?:\r?\n|\r)(?:={3,}|-{3,}|~{3,}|\^{3,}|\+{3,})$|^={1,5} .+|^\.(?![\s.]).*/m,alias:"important",inside:{punctuation:/^(?:\.|=+)|(?:=+|-+|~+|\^+|\++)$/}},"attribute-entry":{pattern:/^:[^:\r\n]+:(?: .*?(?: \+(?:\r?\n|\r).*?)*)?$/m,alias:"tag"},attributes:t,hr:{pattern:/^'{3,}$/m,alias:"punctuation"},"page-break":{pattern:/^<{3,}$/m,alias:"punctuation"},admonition:{pattern:/^(?:CAUTION|IMPORTANT|NOTE|TIP|WARNING):/m,alias:"keyword"},callout:[{pattern:/(^[ \t]*)\d*>/m,lookbehind:!0,alias:"symbol"},{pattern:/<\d+>/,alias:"symbol"}],macro:{pattern:/\b[a-z\d][a-z\d-]*::?(?:[^\s\[\]]*\[(?:[^\]\\"']|(["'])(?:(?!\1)[^\\]|\\.)*\1|\\.)*\])/,inside:{function:/^[a-z\d-]+(?=:)/,punctuation:/^::?/,attributes:{pattern:/(?:\[(?:[^\]\\"']|(["'])(?:(?!\1)[^\\]|\\.)*\1|\\.)*\])/,inside:t.inside}}},inline:{pattern:/(^|[^\\])(?:(?:\B\[(?:[^\]\\"']|(["'])(?:(?!\2)[^\\]|\\.)*\2|\\.)*\])?(?:\b_(?!\s)(?: _|[^_\\\r\n]|\\.)+(?:(?:\r?\n|\r)(?: _|[^_\\\r\n]|\\.)+)*_\b|\B``(?!\s).+?(?:(?:\r?\n|\r).+?)*''\B|\B`(?!\s)(?:[^`'\s]|\s+\S)+['`]\B|\B(['*+#])(?!\s)(?: \3|(?!\3)[^\\\r\n]|\\.)+(?:(?:\r?\n|\r)(?: \3|(?!\3)[^\\\r\n]|\\.)+)*\3\B)|(?:\[(?:[^\]\\"']|(["'])(?:(?!\4)[^\\]|\\.)*\4|\\.)*\])?(?:(__|\*\*|\+\+\+?|##|\$\$|[~^]).+?(?:(?:\r?\n|\r).+?)*\5|\{[^}\r\n]+\}|\[\[\[?.+?(?:(?:\r?\n|\r).+?)*\]?\]\]|<<.+?(?:(?:\r?\n|\r).+?)*>>|\(\(\(?.+?(?:(?:\r?\n|\r).+?)*\)?\)\)))/m,lookbehind:!0,inside:{attributes:t,url:{pattern:/^(?:\[\[\[?.+?\]?\]\]|<<.+?>>)$/,inside:{punctuation:/^(?:\[\[\[?|<<)|(?:\]\]\]?|>>)$/}},"attribute-ref":{pattern:/^\{.+\}$/,inside:{variable:{pattern:/(^\{)[a-z\d,+_-]+/,lookbehind:!0},operator:/^[=?!#%@$]|!(?=[:}])/,punctuation:/^\{|\}$|::?/}},italic:{pattern:/^(['_])[\s\S]+\1$/,inside:{punctuation:/^(?:''?|__?)|(?:''?|__?)$/}},bold:{pattern:/^\*[\s\S]+\*$/,inside:{punctuation:/^\*\*?|\*\*?$/}},punctuation:/^(?:``?|\+{1,3}|##?|\$\$|[~^]|\(\(\(?)|(?:''?|\+{1,3}|##?|\$\$|[~^`]|\)?\)\))$/}},replacement:{pattern:/\((?:C|R|TM)\)/,alias:"builtin"},entity:/?[\da-z]{1,8};/i,"line-continuation":{pattern:/(^| )\+$/m,lookbehind:!0,alias:"punctuation"}};function a(e){e=e.split(" ");for(var t={},a=0,r=e.length;a >=?|<<=?|&&?|\|\|?|[-+*/%&|^!=<>?]=?/,punctuation:/[(),:]/}}e.exports=t,t.displayName="asmatmel",t.aliases=[]},82592:function(e,t,n){"use strict";var a=n(53494);function r(e){e.register(a),e.languages.aspnet=e.languages.extend("markup",{"page-directive":{pattern:/<%\s*@.*%>/,alias:"tag",inside:{"page-directive":{pattern:/<%\s*@\s*(?:Assembly|Control|Implements|Import|Master(?:Type)?|OutputCache|Page|PreviousPageType|Reference|Register)?|%>/i,alias:"tag"},rest:e.languages.markup.tag.inside}},directive:{pattern:/<%.*%>/,alias:"tag",inside:{directive:{pattern:/<%\s*?[$=%#:]{0,2}|%>/,alias:"tag"},rest:e.languages.csharp}}}),e.languages.aspnet.tag.pattern=/<(?!%)\/?[^\s>\/]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/,e.languages.insertBefore("inside","punctuation",{directive:e.languages.aspnet.directive},e.languages.aspnet.tag.inside["attr-value"]),e.languages.insertBefore("aspnet","comment",{"asp-comment":{pattern:/<%--[\s\S]*?--%>/,alias:["asp","comment"]}}),e.languages.insertBefore("aspnet",e.languages.javascript?"script":"tag",{"asp-script":{pattern:/(LiteLLM Dashboard