Merge branch 'BerriAI:main' into main

This commit is contained in:
AnilAren 2025-10-21 10:41:31 +05:30 committed by GitHub
commit d7c3306c7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
209 changed files with 6118 additions and 798 deletions

View file

@ -1419,6 +1419,49 @@ jobs:
paths:
- logging_coverage.xml
- logging_coverage
audio_testing:
docker:
- image: cimg/python:3.11
auth:
username: ${DOCKERHUB_USERNAME}
password: ${DOCKERHUB_PASSWORD}
working_directory: ~/project
steps:
- checkout
- setup_google_dns
- run:
name: Install Dependencies
command: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
pip install "pytest==7.3.1"
pip install "pytest-retry==1.6.3"
pip install "pytest-cov==5.0.0"
pip install "pytest-asyncio==0.21.1"
pip install "respx==0.22.0"
# Run pytest and generate JUnit XML report
- run:
name: Run tests
command: |
pwd
ls
python -m pytest -vv tests/audio_tests --cov=litellm --cov-report=xml -x -s -v --junitxml=test-results/junit.xml --durations=5
no_output_timeout: 120m
- run:
name: Rename the coverage files
command: |
mv coverage.xml audio_coverage.xml
mv .coverage audio_coverage
# Store test results
- store_test_results:
path: test-results
- persist_to_workspace:
root: .
paths:
- audio_coverage.xml
- audio_coverage
installing_litellm_on_python:
docker:
- image: circleci/python:3.8
@ -2784,7 +2827,7 @@ jobs:
python -m venv venv
. venv/bin/activate
pip install coverage
coverage combine llm_translation_coverage llm_responses_api_coverage ocr_coverage mcp_coverage logging_coverage litellm_router_coverage local_testing_coverage litellm_assistants_api_coverage auth_ui_unit_tests_coverage langfuse_coverage caching_coverage litellm_proxy_unit_tests_coverage image_gen_coverage pass_through_unit_tests_coverage batches_coverage litellm_security_tests_coverage guardrails_coverage
coverage combine llm_translation_coverage llm_responses_api_coverage ocr_coverage mcp_coverage logging_coverage audio_coverage litellm_router_coverage local_testing_coverage litellm_assistants_api_coverage auth_ui_unit_tests_coverage langfuse_coverage caching_coverage litellm_proxy_unit_tests_coverage image_gen_coverage pass_through_unit_tests_coverage batches_coverage litellm_security_tests_coverage guardrails_coverage
coverage xml
- codecov/upload:
file: ./coverage.xml
@ -3380,6 +3423,12 @@ workflows:
only:
- main
- /litellm_.*/
- audio_testing:
filters:
branches:
only:
- main
- /litellm_.*/
- upload-coverage:
requires:
- llm_translation_testing
@ -3395,6 +3444,7 @@ workflows:
- pass_through_unit_testing
- image_gen_testing
- logging_testing
- audio_testing
- litellm_router_testing
- litellm_router_unit_testing
- caching_unit_tests
@ -3458,6 +3508,7 @@ workflows:
- pass_through_unit_testing
- image_gen_testing
- logging_testing
- audio_testing
- litellm_router_testing
- litellm_router_unit_testing
- caching_unit_tests

View file

@ -88,6 +88,7 @@ def get_docker_run_command(release_version):
if __name__ == "__main__":
return
csv_file = "load_test_stats.csv" # Change this to the path of your CSV file
markdown_table = interpret_results(csv_file)

View file

@ -18,7 +18,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.4.6
version: 0.4.7
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to

View file

@ -27,6 +27,10 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "litellm.serviceAccountName" . }}
containers:
- name: prisma-migrations

View file

@ -309,33 +309,30 @@ curl http://0.0.0.0:4000/v1/chat/completions \
{"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
],
"response_format": {
"type": "json_object",
"response_schema": {
"type": "json_schema",
"json_schema": {
"name": "math_reasoning",
"schema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"required": ["explanation", "output"],
"additionalProperties": false
}
"type": "json_schema",
"json_schema": {
"name": "math_reasoning",
"schema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"strict": true
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
},
"strict": true
}
},
}'

View file

@ -18,7 +18,7 @@ LiteLLM integrates with vector stores, allowing your models to access your organ
## Supported Vector Stores
- [Bedrock Knowledge Bases](https://aws.amazon.com/bedrock/knowledge-bases/)
- [OpenAI Vector Stores](https://platform.openai.com/docs/api-reference/vector-stores/search)
- [Azure Vector Stores](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/file-search?tabs=python#vector-stores)
- [Azure Vector Stores](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/file-search?tabs=python#vector-stores) (Cannot be directly queried. Only available for calling in Assistants messages. We will be adding Azure AI Search Vector Store API support soon.)
- [Vertex AI RAG API](https://cloud.google.com/vertex-ai/generative-ai/docs/rag-overview)
## Quick Start

View file

@ -506,3 +506,11 @@ curl -L -X GET 'http://0.0.0.0:4000/v1/model/info' \
</Tabs>
This checks our maintained [model info/cost map](https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json)
## Read More
:::tip Auto-Inject Prompt Caching
Want LiteLLM to automatically add `cache_control` directives without modifying your code?
See [**Auto-Inject Prompt Caching Tutorial**](../tutorials/prompt_caching.md) to learn how to use `cache_control_injection_points` to automatically cache system messages, specific messages by index, or custom injection patterns.
:::

View file

@ -11,7 +11,7 @@ import TabItem from '@theme/TabItem';
|-------|-------|
| Description | Azure OpenAI Service provides REST API access to OpenAI's powerful language models including o1, o1-mini, GPT-5, GPT-4o, GPT-4o mini, GPT-4 Turbo with Vision, GPT-4, GPT-3.5-Turbo, and Embeddings model series |
| Provider Route on LiteLLM | `azure/`, [`azure/o_series/`](#o-series-models), [`azure/gpt5_series/`](#gpt-5-models) |
| Supported Operations | [`/chat/completions`](#azure-openai-chat-completion-models), [`/responses`](./azure_responses), [`/completions`](#azure-instruct-models), [`/embeddings`](./azure_embedding), [`/audio/speech`](#azure-text-to-speech-tts), [`/audio/transcriptions`](../audio_transcription), `/fine_tuning`, [`/batches`](#azure-batches-api), `/files`, [`/images`](../image_generation#azure-openai-image-generation-models) |
| Supported Operations | [`/chat/completions`](#azure-openai-chat-completion-models), [`/responses`](./azure_responses), [`/completions`](#azure-instruct-models), [`/embeddings`](./azure_embedding), [`/audio/speech`](azure_speech), [`/audio/transcriptions`](../audio_transcription), `/fine_tuning`, [`/batches`](#azure-batches-api), `/files`, [`/images`](../image_generation#azure-openai-image-generation-models) |
| Link to Provider Doc | [Azure OpenAI ↗](https://learn.microsoft.com/en-us/azure/ai-services/openai/overview)
## API Keys, Params
@ -538,39 +538,6 @@ response = litellm.completion(
print(response)
```
## Azure Text to Speech (tts)
**LiteLLM PROXY**
```yaml
- model_name: azure/tts-1
litellm_params:
model: azure/tts-1
api_base: "os.environ/AZURE_API_BASE_TTS"
api_key: "os.environ/AZURE_API_KEY_TTS"
api_version: "os.environ/AZURE_API_VERSION"
```
**LiteLLM SDK**
```python
from litellm import completion
## set ENV variables
os.environ["AZURE_API_KEY"] = ""
os.environ["AZURE_API_BASE"] = ""
os.environ["AZURE_API_VERSION"] = ""
# azure call
speech_file_path = Path(__file__).parent / "speech.mp3"
response = speech(
model="azure/<your-deployment-name",
voice="alloy",
input="the quick brown fox jumped over the lazy dogs",
)
response.stream_to_file(speech_file_path)
```
## **Authentication**

View file

@ -0,0 +1,68 @@
# Azure Text to Speech (tts)
Convert text to natural-sounding speech using Azure OpenAI's Text to Speech models. Supports multiple voices and audio formats.
## Quick Start
**LiteLLM SDK**
```python showLineNumbers title="SDK Usage"
from litellm import speech
from pathlib import Path
import os
## set ENV variables
os.environ["AZURE_API_KEY"] = ""
os.environ["AZURE_API_BASE"] = ""
os.environ["AZURE_API_VERSION"] = ""
# azure call
speech_file_path = Path(__file__).parent / "speech.mp3"
response = speech(
model="azure/<your-deployment-name>",
voice="alloy",
input="the quick brown fox jumped over the lazy dogs",
)
response.stream_to_file(speech_file_path)
```
**LiteLLM PROXY**
```yaml showLineNumbers title="proxy_config.yaml"
model_list:
- model_name: azure/tts-1
litellm_params:
model: azure/tts-1
api_base: "os.environ/AZURE_API_BASE_TTS"
api_key: "os.environ/AZURE_API_KEY_TTS"
api_version: "os.environ/AZURE_API_VERSION"
```
## Available Voices
Azure OpenAI supports the following voices:
- `alloy` - Neutral and balanced
- `echo` - Warm and upbeat
- `fable` - Expressive and dramatic
- `onyx` - Deep and authoritative
- `nova` - Friendly and conversational
- `shimmer` - Bright and cheerful
## Supported Parameters
```python showLineNumbers title="All Parameters"
response = speech(
model="azure/<your-deployment-name>",
voice="alloy", # Required: Voice selection
input="text to convert", # Required: Input text
speed=1.0, # Optional: 0.25 to 4.0 (default: 1.0)
response_format="mp3" # Optional: mp3, opus, aac, flac, wav, pcm
)
```
## Supported Models
- `tts-1` - Standard quality, optimized for speed
- `tts-1-hd` - High definition, optimized for quality
Use your Azure deployment name: `azure/<your-deployment-name>`

View file

@ -0,0 +1,212 @@
# Azure AI Speech (Cognitive Services)
Azure AI Speech is Azure's Cognitive Services text-to-speech API, separate from Azure OpenAI. It provides high-quality neural voices with broader language support and advanced speech customization.
**When to use this vs Azure OpenAI TTS:**
- **Azure AI Speech** - More languages, neural voices, SSML support, speech customization
- **Azure OpenAI TTS** - OpenAI models, integrated with Azure OpenAI services
## Overview
| Property | Details |
|-------|-------|
| Description | Azure AI Speech is Azure's Cognitive Services text-to-speech API, separate from Azure OpenAI. It provides high-quality neural voices with broader language support and advanced speech customization. |
| Provider Route on LiteLLM | `azure/speech/` |
## Quick Start
**LiteLLM SDK**
```python showLineNumbers title="SDK Usage"
from litellm import speech
from pathlib import Path
import os
os.environ["AZURE_TTS_API_KEY"] = "your-cognitive-services-key"
speech_file_path = Path(__file__).parent / "speech.mp3"
response = speech(
model="azure/speech/azure-tts",
voice="alloy",
input="Hello, this is Azure AI Speech",
api_base="https://eastus.tts.speech.microsoft.com",
api_key=os.environ["AZURE_TTS_API_KEY"],
)
response.stream_to_file(speech_file_path)
```
**LiteLLM Proxy**
```yaml showLineNumbers title="proxy_config.yaml"
model_list:
- model_name: azure-speech
litellm_params:
model: azure/speech/azure-tts
api_base: https://eastus.tts.speech.microsoft.com
api_key: os.environ/AZURE_TTS_API_KEY
```
## Setup
1. Create an Azure Cognitive Services resource in the [Azure Portal](https://portal.azure.com)
2. Get your API key from the resource
3. Note your region (e.g., `eastus`, `westus`, `westeurope`)
4. Use the regional endpoint: `https://{region}.tts.speech.microsoft.com`
## Cost Tracking (Pricing)
LiteLLM automatically tracks costs for Azure AI Speech based on the number of characters processed.
### Available Models
| Model | Voice Type | Cost per 1M Characters |
|-------|-----------|----------------------|
| `azure/speech/azure-tts` | Neural | $15 |
| `azure/speech/azure-tts-hd` | Neural HD | $30 |
### How Costs are Calculated
Azure AI Speech charges based on the number of characters in your input text. LiteLLM automatically:
- Counts the number of characters in your `input` parameter
- Calculates the cost based on the model pricing
- Returns the cost in the response object
```python showLineNumbers title="View Request Cost"
from litellm import speech
response = speech(
model="azure/speech/azure-tts",
voice="alloy",
input="Hello, this is a test message",
api_base="https://eastus.tts.speech.microsoft.com",
api_key=os.environ["AZURE_TTS_API_KEY"],
)
# Access the calculated cost
cost = response._hidden_params.get("response_cost")
print(f"Request cost: ${cost}")
```
### Verify Azure Pricing
To check the latest Azure AI Speech pricing:
1. Visit the [Azure Pricing Calculator](https://azure.microsoft.com/en-us/pricing/calculator/)
2. Set **Service** to "AI Services"
3. Set **API** to "Azure AI Speech"
4. Select **Text to Speech** and your region
5. View the current pricing per million characters
**Note:** Pricing may vary by region and Azure subscription type.
## Voice Mapping
LiteLLM automatically maps OpenAI voice names to Azure Neural voices:
| OpenAI Voice | Azure Neural Voice | Description |
|-------------|-------------------|-------------|
| `alloy` | en-US-JennyNeural | Neutral and balanced |
| `echo` | en-US-GuyNeural | Warm and upbeat |
| `fable` | en-GB-RyanNeural | Expressive and dramatic |
| `onyx` | en-US-DavisNeural | Deep and authoritative |
| `nova` | en-US-AmberNeural | Friendly and conversational |
| `shimmer` | en-US-AriaNeural | Bright and cheerful |
## Supported Parameters
```python showLineNumbers title="All Parameters"
response = speech(
model="azure/speech/azure-tts",
voice="alloy", # Required: Voice selection
input="text to convert", # Required: Input text
speed=1.0, # Optional: 0.25 to 4.0 (default: 1.0)
response_format="mp3", # Optional: mp3, opus, wav, pcm
api_base="https://eastus.tts.speech.microsoft.com",
api_key="your-key",
)
```
### Response Formats
| Format | Azure Output Format | Sample Rate |
|--------|-------------------|-------------|
| `mp3` | audio-24khz-48kbitrate-mono-mp3 | 24kHz |
| `opus` | ogg-48khz-16bit-mono-opus | 48kHz |
| `wav` | riff-24khz-16bit-mono-pcm | 24kHz |
| `pcm` | raw-24khz-16bit-mono-pcm | 24kHz |
## Async Support
```python showLineNumbers title="Async Usage"
import asyncio
from litellm import aspeech
from pathlib import Path
async def generate_speech():
response = await aspeech(
model="azure/speech/azure-tts",
voice="alloy",
input="Hello from async",
api_base="https://eastus.tts.speech.microsoft.com",
api_key=os.environ["AZURE_TTS_API_KEY"],
)
speech_file_path = Path(__file__).parent / "speech.mp3"
response.stream_to_file(speech_file_path)
asyncio.run(generate_speech())
```
## Regional Endpoints
Replace `{region}` with your Azure resource region:
- US East: `https://eastus.tts.speech.microsoft.com`
- US West: `https://westus.tts.speech.microsoft.com`
- Europe West: `https://westeurope.tts.speech.microsoft.com`
- Asia Southeast: `https://southeastasia.tts.speech.microsoft.com`
[Full list of regions](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/regions)
## Advanced Features
### Custom Neural Voices
You can use any Azure Neural voice by passing the full voice name:
```python showLineNumbers title="Custom Voice"
response = speech(
model="azure/speech/azure-tts",
voice="en-US-AriaNeural", # Direct Azure voice name
input="Using a specific neural voice",
api_base="https://eastus.tts.speech.microsoft.com",
api_key=os.environ["AZURE_TTS_API_KEY"],
)
```
Browse available voices in the [Azure Speech Gallery](https://speech.microsoft.com/portal/voicegallery).
## Error Handling
```python showLineNumbers title="Error Handling"
from litellm import speech
from litellm.exceptions import APIError
try:
response = speech(
model="azure/speech/azure-tts",
voice="alloy",
input="Test message",
api_base="https://eastus.tts.speech.microsoft.com",
api_key=os.environ["AZURE_TTS_API_KEY"],
)
except APIError as e:
print(f"Azure Speech error: {e}")
```
## Reference
- [Azure Speech Service Documentation](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/)
- [Text-to-Speech REST API](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech)

View file

@ -1,3 +1,6 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Clarifai
Anthropic, OpenAI, Qwen, xAI, Gemini and most of Open soured LLMs are Supported on Clarifai.

View file

@ -470,6 +470,7 @@ router_settings:
| DEFAULT_MAX_RETRIES | Default maximum retry attempts. Default is 2
| DEFAULT_MAX_TOKENS | Default maximum tokens for LLM calls. Default is 4096
| DEFAULT_MAX_TOKENS_FOR_TRITON | Default maximum tokens for Triton models. Default is 2000
| DEFAULT_MAX_REDIS_BATCH_CACHE_SIZE | Default maximum size for redis batch cache. Default is 1000
| DEFAULT_MOCK_RESPONSE_COMPLETION_TOKEN_COUNT | Default token count for mock response completions. Default is 20
| DEFAULT_MOCK_RESPONSE_PROMPT_TOKEN_COUNT | Default token count for mock response prompts. Default is 10
| DEFAULT_MODEL_CREATED_AT_TIME | Default creation timestamp for models. Default is 1677610602
@ -717,6 +718,7 @@ router_settings:
| PROXY_BATCH_POLLING_INTERVAL | Time in seconds to wait before polling a batch, to check if it's completed. Default is 6000s (1 hour)
| 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
| PYTHON_GC_THRESHOLD | GC thresholds ('gen0,gen1,gen2', e.g. '1000,50,50'); defaults to Pythons values.
| PROXY_LOGOUT_URL | URL for logging out of the proxy service
| QDRANT_API_BASE | Base URL for Qdrant API
| QDRANT_API_KEY | API key for Qdrant service
@ -732,6 +734,7 @@ router_settings:
| REDIS_GCP_SSL_CA_CERTS | Path to SSL CA certificate file for secure GCP Memorystore Redis connections
| REDOC_URL | The path to the Redoc Fast API documentation. **By default this is "/redoc"**
| REPEATED_STREAMING_CHUNK_LIMIT | Limit for repeated streaming chunks to detect looping. Default is 100
| REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES | Maximum size in bytes for WebSocket messages in realtime connections. Default is None.
| REPLICATE_MODEL_NAME_WITH_ID_LENGTH | Length of Replicate model names with ID. Default is 64
| REPLICATE_POLLING_DELAY_SECONDS | Delay in seconds for Replicate polling operations. Default is 0.5
| REQUEST_TIMEOUT | Timeout in seconds for requests. Default is 6000

View file

@ -141,6 +141,7 @@ LiteLLM allows you to customize various aspects of your email notifications. Bel
| Email Signature | `EMAIL_SIGNATURE` | string (HTML) | Standard LiteLLM footer | `"<p>Best regards,<br/>Your Team</p><p><a href='https://your-company.com'>Visit us</a></p>"` | 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 |
| Proxy Base URL | `PROXY_BASE_URL` | string | http://0.0.0.0:4000 | `"https://proxy.your-company.com"` | Base URL for the LiteLLM Proxy (used in email links) |
## HTML Support in Email Signature
@ -180,6 +181,9 @@ EMAIL_SIGNATURE="<p>Best regards,<br/>Your Company Team</p><p><a href='https://y
# 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
# Proxy Configuration
PROXY_BASE_URL="https://proxy.your-company.com" # Base URL for the LiteLLM Proxy (used in email links)
```
## HTML Support in Email Signature
@ -225,3 +229,17 @@ 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.
## FAQ
### Why do I see "http://0.0.0.0:4000" in the email links?
The `PROXY_BASE_URL` environment variable is used to construct email links. If you are using the LiteLLM Proxy in a local environment, you will see "http://0.0.0.0:4000" in the email links.
If you are using the LiteLLM Proxy in a production environment, you will see the actual base URL of the LiteLLM Proxy.
You can set the `PROXY_BASE_URL` environment variable to the actual base URL of the LiteLLM Proxy.
```bash
PROXY_BASE_URL="https://proxy.your-company.com"
```

View file

@ -11,10 +11,13 @@ LiteLLM supports PANW Prisma AIRS (AI Runtime Security) guardrails via the [Pris
- ✅ **Real-time prompt injection detection**
- ✅ **Malicious content filtering**
- ✅ **Data loss prevention (DLP)**
- ✅ **Sensitive content masking** - Automatically mask PII, credit cards, SSNs instead of blocking
- ✅ **Comprehensive threat detection** for AI models and datasets
- ✅ **Model-agnostic protection** across public and private models
- ✅ **Synchronous scanning** with immediate response
- ✅ **Configurable security profiles**
- ✅ **Streaming support** - Real-time masking for streaming responses
- ✅ **Fail-closed security** - Blocks requests if PANW API is unavailable (maximum security)
## Quick Start
@ -42,9 +45,9 @@ guardrails:
litellm_params:
guardrail: panw_prisma_airs
mode: "pre_call" # Run before LLM call
api_key: os.environ/AIRS_API_KEY # Your PANW API key
profile_name: os.environ/AIRS_API_PROFILE_NAME # Security profile from Strata Cloud Manager
api_base: "https://service.api.aisecurity.paloaltonetworks.com/v1/scan/sync/request" # Optional
api_key: os.environ/PANW_PRISMA_AIRS_API_KEY # Your Prisma AIRS API key
profile_name: os.environ/PANW_PRISMA_AIRS_PROFILE_NAME # Security profile from Strata Cloud Manager
api_base: "https://service.api.aisecurity.paloaltonetworks.com"
```
#### Supported values for `mode`
@ -56,8 +59,8 @@ guardrails:
### 3. Start LiteLLM Gateway
```bash title="Set environment variables"
export AIRS_API_KEY="your-panw-api-key"
export AIRS_API_PROFILE_NAME="your-security-profile"
export PANW_PRISMA_AIRS_API_KEY="your-panw-api-key"
export PANW_PRISMA_AIRS_PROFILE_NAME="your-security-profile"
export OPENAI_API_KEY="sk-proj-..."
```
@ -197,16 +200,16 @@ Expected successful response:
|-----------|----------|-------------|---------|
| `api_key` | Yes | Your PANW Prisma AIRS API key from Strata Cloud Manager | - |
| `profile_name` | Yes | Security profile name configured in Strata Cloud Manager | - |
| `api_base` | No | Custom API endpoint | `https://service.api.aisecurity.paloaltonetworks.com/v1/scan/sync/request` |
| `api_base` | No | Custom API base URL (without /v1/scan/sync/request path) | `https://service.api.aisecurity.paloaltonetworks.com` |
| `mode` | No | When to run the guardrail | `pre_call` |
## Environment Variables
```bash
export AIRS_API_KEY="your-panw-api-key"
export AIRS_API_PROFILE_NAME="your-security-profile"
# Optional custom endpoint
export PANW_API_ENDPOINT="https://custom-endpoint.com/v1/scan/sync/request"
export PANW_PRISMA_AIRS_API_KEY="your-panw-api-key"
export PANW_PRISMA_AIRS_PROFILE_NAME="your-security-profile"
# Optional custom base URL (without /v1/scan/sync/request path)
export PANW_PRISMA_AIRS_API_BASE="https://custom-endpoint.com"
```
## Advanced Configuration
@ -221,17 +224,125 @@ guardrails:
litellm_params:
guardrail: panw_prisma_airs
mode: "pre_call"
api_key: os.environ/AIRS_API_KEY
api_key: os.environ/PANW_PRISMA_AIRS_API_KEY
profile_name: "strict-policy" # High security profile
- guardrail_name: "panw-permissive-security"
litellm_params:
guardrail: panw_prisma_airs
mode: "post_call"
api_key: os.environ/AIRS_API_KEY
api_key: os.environ/PANW_PRISMA_AIRS_API_KEY
profile_name: "permissive-policy" # Lower security profile
```
### Content Masking
PANW Prisma AIRS can automatically mask sensitive content (PII, credit cards, SSNs, etc.) instead of blocking requests. This allows your application to continue functioning while protecting sensitive data.
#### How It Works
1. **Detection**: PANW scans content and identifies sensitive data
2. **Masking**: Sensitive data is replaced with placeholders (e.g., `XXXXXXXXXX` or `{PHONE}`)
3. **Pass-through**: Masked content is sent to the LLM or returned to the user
#### Configuration Options
```yaml
guardrails:
- guardrail_name: "panw-with-masking"
litellm_params:
guardrail: panw_prisma_airs
mode: "post_call" # Scan both input and output
api_key: os.environ/PANW_PRISMA_AIRS_API_KEY
profile_name: "default"
mask_request_content: true # Mask sensitive data in prompts
mask_response_content: true # Mask sensitive data in responses
```
**Masking Parameters:**
- `mask_request_content: true` - When PANW detects sensitive data in prompts, mask it instead of blocking
- `mask_response_content: true` - When PANW detects sensitive data in responses, mask it instead of blocking
- `mask_on_block: true` - Backwards compatible flag that enables both request and response masking
:::warning Important: Masking is Controlled by PANW Security Profile
The **actual masking behavior** (what content gets masked and how) is controlled by your **PANW Prisma AIRS security profile** configured in Strata Cloud Manager. The LiteLLM config settings (`mask_request_content`, `mask_response_content`) only control whether to:
- **Apply the masked content** returned by PANW and allow the request to continue, OR
- **Block the request** entirely when sensitive data is detected
LiteLLM does not alter or configure your PANW security profile. To change what content gets masked, update your profile settings in Strata Cloud Manager.
:::
:::info Security Posture
The guardrail is **fail-closed** by default - if the PANW API is unavailable, requests are blocked to ensure no unscanned content reaches your LLM. This provides maximum security.
:::
#### Example: Masking Credit Card Numbers
<Tabs>
<TabItem label="Without Masking" value="no-mask">
**Request:**
```json
{
"messages": [
{"role": "user", "content": "My credit card is 4929-3813-3266-4295"}
]
}
```
**Response:** ❌ **Blocked with 400 error**
</TabItem>
<TabItem label="With Masking" value="with-mask">
**Request:**
```json
{
"messages": [
{"role": "user", "content": "My credit card is 4929-3813-3266-4295"}
]
}
```
**Masked prompt sent to LLM:**
```json
{
"messages": [
{"role": "user", "content": "My credit card is XXXXXXXXXXXXXXXXXX"}
]
}
```
**Response:** ✅ **Allowed with masked content**
</TabItem>
</Tabs>
#### Masking Capabilities
The guardrail masks sensitive content in:
- ✅ **Chat messages** - User prompts and assistant responses
- ✅ **Streaming responses** - Real-time masking of streamed content
- ✅ **Multi-choice responses** - All choices in the response
- ✅ **Tool/function calls** - Arguments passed to tools and functions
- ✅ **Content lists** - Mixed content types (text, images, etc.)
#### Complete Example
```yaml
guardrails:
- guardrail_name: "panw-production-security"
litellm_params:
guardrail: panw_prisma_airs
mode: "post_call" # Scan input and output
api_key: os.environ/PANW_PRISMA_AIRS_API_KEY
profile_name: "production-profile"
mask_request_content: true # Mask sensitive prompts
mask_response_content: true # Mask sensitive responses
```
## Use Cases
From [official Prisma AIRS documentation](https://docs.paloaltonetworks.com/ai-runtime-security/activation-and-onboarding/ai-runtime-security-api-intercept-overview):
@ -245,7 +356,7 @@ From [official Prisma AIRS documentation](https://docs.paloaltonetworks.com/ai-r
## Next Steps
- Configure your security policies in [Strata Cloud Manager](https://apps.paloaltonetworks.com/)
- Review the [Prisma AIRS API documentation](https://pan.dev/prisma-airs/api/airuntimesecurity/scan-sync-request/) for advanced features
- Review the [Prisma AIRS API documentation](https://pan.dev/airs/) for advanced features
- Set up monitoring and alerting for threat detections in your PANW dashboard
- Consider implementing both pre_call and post_call guardrails for comprehensive protection
- Monitor detection events and tune your security profiles based on your application needs

View file

@ -62,20 +62,20 @@ These specifications provide:
- Adequate memory for request processing and caching
## 3. On Kubernetes - Use 1 Uvicorn worker [Suggested CMD]
## 3. On Kubernetes — Match Uvicorn Workers to CPU Count [Suggested CMD]
Use this Docker `CMD`. This will start the proxy with 1 Uvicorn Async Worker
Use this Docker `CMD`. It automatically matches Uvicorn workers to the pods CPU count, ensuring each worker uses one core efficiently for better throughput and stable latency.
(Ensure that you're not setting `run_gunicorn` or `num_workers` in the CMD).
```shell
CMD ["--port", "4000", "--config", "./proxy_server_config.yaml"]
CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--num_workers", "$(nproc)"]
```
> Optional: If you observe gradual memory growth under sustained load, consider recycling workers after a fixed number of requests to mitigate leaks. Set this via CLI or environment variable:
> **Optional:** If you observe gradual memory growth under sustained load, consider recycling workers after a fixed number of requests to mitigate leaks.
> You can configure this either via CLI or environment variable:
```shell
# CLI
CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--max_requests_before_restart", "10000"]
CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--num_workers", "$(nproc)", "--max_requests_before_restart", "10000"]
# or ENV (for deployment manifests / containers)
export MAX_REQUESTS_BEFORE_RESTART=10000

View file

@ -4,6 +4,19 @@ import TabItem from '@theme/TabItem';
# /audio/speech
## Overview
| Feature | Supported | Notes |
|-------|-------|-------|
| Cost Tracking | ✅ | |
| Logging | ✅ | works across all integrations |
| End-user Tracking | ✅ | |
| Fallbacks | ✅ | between supported models |
| Loadbalancing | ✅ | between supported models |
| Guardrails | ❌ Please make an [issue if you need this feature](https://github.com/BerriAI/litellm/issues/new) | |
| Support llm providers | | `openai`, `azure`, `azure_ai`, `vertex_ai`, `gemini`, etc. |
## **LiteLLM Python SDK Usage**
### Quick Start
@ -88,6 +101,7 @@ litellm --config /path/to/config.yaml
|-------------|--------------------|
| OpenAI | [Usage](#quick-start) |
| Azure OpenAI| [Usage](../docs/providers/azure#azure-text-to-speech-tts) |
| Azure AI Speech Service (AVA)| [Usage](../docs/providers/azure_ai_speech) |
| Vertex AI | [Usage](../docs/providers/vertex#text-to-speech-apis) |
| Gemini | [Usage](#gemini-text-to-speech) |

View file

@ -24,15 +24,174 @@ You need to specify `cache_control_injection_points` in your model configuration
LiteLLM will then automatically add a `cache_control` directive to the specified messages in your requests:
```json
```json showLineNumbers title="cache_control_directive.json"
"cache_control": {
"type": "ephemeral"
}
```
## Usage Example
## LiteLLM Python SDK Usage
In this example, we'll configure caching for system messages by adding the directive to all messages with `role: system`.
Use the `cache_control_injection_points` parameter in your completion calls to automatically inject caching directives.
#### Basic Example - Cache System Messages
```python showLineNumbers title="cache_system_messages.py"
from litellm import completion
import os
os.environ["ANTHROPIC_API_KEY"] = ""
response = completion(
model="anthropic/claude-3-5-sonnet-20240620",
messages=[
{
"role": "system",
"content": [
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing legal documents.",
},
{
"type": "text",
"text": "Here is the full text of a complex legal agreement" * 400,
},
],
},
{
"role": "user",
"content": "what are the key terms and conditions in this agreement?",
},
],
# Auto-inject cache control to system messages
cache_control_injection_points=[
{
"location": "message",
"role": "system",
}
],
)
print(response.usage)
```
**Key Points:**
- Use `cache_control_injection_points` parameter to specify where to inject caching
- `location: "message"` targets messages in the conversation
- `role: "system"` targets all system messages
- LiteLLM automatically adds `cache_control` to the **last content block** of matching messages (per Anthropic's API specification)
**LiteLLM's Modified Request:**
LiteLLM automatically transforms your request by adding `cache_control` to the last content block of the system message:
```json showLineNumbers title="modified_request_system.json"
{
"messages": [
{
"role": "system",
"content": [
{
"type": "text",
"text": "You are an AI assistant tasked with analyzing legal documents."
},
{
"type": "text",
"text": "Here is the full text of a complex legal agreement...",
"cache_control": {"type": "ephemeral"} // Added by LiteLLM
}
]
},
{
"role": "user",
"content": "what are the key terms and conditions in this agreement?"
}
]
}
```
#### Target Specific Messages by Index
You can target specific messages by their index in the messages array. Use negative indices to target from the end.
```python showLineNumbers title="cache_by_index.py"
from litellm import completion
import os
os.environ["ANTHROPIC_API_KEY"] = ""
response = completion(
model="anthropic/claude-3-5-sonnet-20240620",
messages=[
{
"role": "user",
"content": "First message",
},
{
"role": "assistant",
"content": "Response to first",
},
{
"role": "user",
"content": [
{"type": "text", "text": "Here is a long document to analyze:"},
{"type": "text", "text": "Document content..." * 500},
],
},
],
# Target the last message (index -1)
cache_control_injection_points=[
{
"location": "message",
"index": -1, # -1 targets the last message, -2 would target second-to-last, etc.
}
],
)
print(response.usage)
```
**Important Notes:**
- When a message has multiple content blocks (like images or multiple text blocks), `cache_control` is only added to the **last content block**
- This follows [Anthropic's API specification](https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#continuing-a-multi-turn-conversation) which requires: "When using multiple content blocks, only the last content block can have cache_control"
- Anthropic has a maximum of 4 blocks with `cache_control` per request
**LiteLLM's Modified Request:**
LiteLLM adds `cache_control` to the last content block of the targeted message (index -1 = last message):
```json showLineNumbers title="modified_request_index.json"
{
"messages": [
{
"role": "user",
"content": "First message"
},
{
"role": "assistant",
"content": "Response to first"
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "Here is a long document to analyze:"
},
{
"type": "text",
"text": "Document content...",
"cache_control": {"type": "ephemeral"} // Added by LiteLLM to last content block only
}
]
}
]
}
```
## LiteLLM Proxy Usage
You can configure cache control injection in the proxy configuration file.
<Tabs>
<TabItem value="litellm config.yaml" label="litellm config.yaml">
@ -64,7 +223,7 @@ On the LiteLLM UI, you can specify the `cache_control_injection_points` in the `
In this example, we have a very long, static system message and a varying user message. It's efficient to cache the system message since it rarely changes.
```json
```json showLineNumbers title="original_request.json"
{
"messages": [
{
@ -93,7 +252,7 @@ In this example, we have a very long, static system message and a varying user m
LiteLLM auto-injects the caching directive into the system message based on our configuration:
```json
```json showLineNumbers title="modified_request.json"
{
"messages": [
{
@ -121,8 +280,9 @@ LiteLLM auto-injects the caching directive into the system message based on our
When the model provider processes this request, it will recognize the caching directive and only process the system message once, caching it for subsequent requests.
## Related Documentation
- [Manual Prompt Caching](../completion/prompt_caching.md) - Learn how to manually add `cache_control` directives to your messages

View file

@ -11,10 +11,6 @@ authors:
title: CTO, LiteLLM
url: https://www.linkedin.com/in/reffajnaahsi/
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
- name: Alexsander Hamir
title: Backend Performance Engineer
url: https://www.linkedin.com/in/alexsander-baptista/
image_url: https://media.licdn.com/dms/image/v2/D5603AQGXnziu4kqNCQ/profile-displayphoto-crop_800_800/B56ZkxEcuOKEAI-/0/1757464874550?e=1762387200&v=beta&t=9SNXLsWhx8OnYPAMQ9fqAr02oevDYEAL2vMYg2f9ieg
hide_table_of_contents: false
---

View file

@ -11,18 +11,6 @@ authors:
title: CTO, LiteLLM
url: https://www.linkedin.com/in/reffajnaahsi/
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
- name: Alexsander Hamir
title: Backend Performance Engineer
url: https://www.linkedin.com/in/alexsander-baptista/
image_url: https://media.licdn.com/dms/image/v2/D5603AQGXnziu4kqNCQ/profile-displayphoto-crop_800_800/B56ZkxEcuOKEAI-/0/1757464874550?e=1762387200&v=beta&t=9SNXLsWhx8OnYPAMQ9fqAr02oevDYEAL2vMYg2f9ieg
- name: Achintya Rajan
title: Fullstack Engineer
url: https://www.linkedin.com/in/achintya-rajan/
image_url: https://media.licdn.com/dms/image/v2/D5603AQGdkEeyJTdljw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1716271140869?e=1762387200&v=beta&t=9gOoLPeqR2E5z3KSX61EUj3HVZXmgo87vhVuSHeffjc
- name: Sameer Kankute
title: Backend Engineer (LLM Translation)
url: https://www.linkedin.com/in/sameer-kankute/
image_url: https://media.licdn.com/dms/image/v2/D4D03AQHB_loQYd5gjg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1719137160975?e=1762387200&v=beta&t=0jbuX-f4eSnDxBY3olI6meuYr-LMbObhFmFbRcKF5mY
hide_table_of_contents: false
---

View file

@ -11,18 +11,6 @@ authors:
title: CTO, LiteLLM
url: https://www.linkedin.com/in/reffajnaahsi/
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
- name: Alexsander Hamir
title: Backend Performance Engineer
url: https://www.linkedin.com/in/alexsander-baptista/
image_url: https://media.licdn.com/dms/image/v2/D5603AQGXnziu4kqNCQ/profile-displayphoto-crop_800_800/B56ZkxEcuOKEAI-/0/1757464874550?e=1762387200&v=beta&t=9SNXLsWhx8OnYPAMQ9fqAr02oevDYEAL2vMYg2f9ieg
- name: Achintya Rajan
title: Fullstack Engineer
url: https://www.linkedin.com/in/achintya-rajan/
image_url: https://media.licdn.com/dms/image/v2/D5603AQGdkEeyJTdljw/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1716271140869?e=1762387200&v=beta&t=9gOoLPeqR2E5z3KSX61EUj3HVZXmgo87vhVuSHeffjc
- name: Sameer Kankute
title: Backend Engineer (LLM Translation)
url: https://www.linkedin.com/in/sameer-kankute/
image_url: https://media.licdn.com/dms/image/v2/D4D03AQHB_loQYd5gjg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1719137160975?e=1762387200&v=beta&t=0jbuX-f4eSnDxBY3olI6meuYr-LMbObhFmFbRcKF5mY
hide_table_of_contents: false
---

View file

@ -0,0 +1,300 @@
---
title: "[Preview] v1.78.5-stable - Native OCR Support"
slug: "v1-78-5"
date: 2025-10-18T10: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 Jaff
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
<Tabs>
<TabItem value="docker" label="Docker">
``` showLineNumbers title="docker run litellm"
docker run \
-e STORE_MODEL_IN_DB=True \
-p 4000:4000 \
ghcr.io/berriai/litellm:v1.78.5.rc.1
```
</TabItem>
<TabItem value="pip" label="Pip">
``` showLineNumbers title="pip install litellm"
pip install litellm==1.78.5
```
</TabItem>
</Tabs>
---
## Key Highlights
- **Native OCR Endpoints** - Native `/v1/ocr` endpoint support with cost tracking for Mistral OCR and Azure AI OCR
- **Global Vendor Discounts** - Specify global vendor discount percentages for accurate cost tracking and reporting
- **Team Spending Reports** - Team admins can now export detailed spending reports for their teams
- **Claude Haiku 4.5** - Day 0 support for Claude Haiku 4.5 across Bedrock, Vertex AI, and OpenRouter with 200K context window
- **GPT-5-Codex** - Support for GPT-5-Codex via Responses API on OpenAI and Azure
- **Performance Improvements** - Major router optimizations: O(1) model lookups, 10-100x faster shallow copy, 30-40% faster timing calls, and O(n) to O(1) hash generation
---
## New Models / Updated Models
#### New Model Support
| Provider | Model | Context Window | Input ($/1M tokens) | Output ($/1M tokens) | Features |
| -------- | ----- | -------------- | ------------------- | -------------------- | -------- |
| Anthropic | `claude-haiku-4-5` | 200K | $1.00 | $5.00 | Chat, reasoning, vision, function calling, prompt caching, computer use |
| Anthropic | `claude-haiku-4-5-20251001` | 200K | $1.00 | $5.00 | Chat, reasoning, vision, function calling, prompt caching, computer use |
| Bedrock | `anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.00 | $5.00 | Chat, reasoning, vision, function calling, prompt caching |
| Bedrock | `global.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.00 | $5.00 | Chat, reasoning, vision, function calling, prompt caching |
| Bedrock | `jp.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.10 | $5.50 | Chat, reasoning, vision, function calling, prompt caching (JP Cross-Region) |
| Bedrock | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.10 | $5.50 | Chat, reasoning, vision, function calling, prompt caching (US region) |
| Bedrock | `eu.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.10 | $5.50 | Chat, reasoning, vision, function calling, prompt caching (EU region) |
| Bedrock | `apac.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.10 | $5.50 | Chat, reasoning, vision, function calling, prompt caching (APAC region) |
| Bedrock | `au.anthropic.claude-haiku-4-5-20251001-v1:0` | 200K | $1.10 | $5.50 | Chat, reasoning, vision, function calling, prompt caching (AU region) |
| Vertex AI | `vertex_ai/claude-haiku-4-5@20251001` | 200K | $1.00 | $5.00 | Chat, reasoning, vision, function calling, prompt caching |
| OpenAI | `gpt-5` | 272K | $1.25 | $10.00 | Chat, responses API, reasoning, vision, function calling, prompt caching |
| OpenAI | `gpt-5-codex` | 272K | $1.25 | $10.00 | Responses API mode |
| Azure | `azure/gpt-5-codex` | 272K | $1.25 | $10.00 | Responses API mode |
| Gemini | `gemini-2.5-flash-image` | 32K | $0.30 | $2.50 | Image generation (GA - Nano Banana) - $0.039/image |
| ZhipuAI | `glm-4.6` | - | - | - | Chat completions |
#### Features
- **[OpenAI](../../docs/providers/openai)**
- GPT-5 return reasoning content via /chat/completions + GPT-5-Codex working on Claude Code - [PR #15441](https://github.com/BerriAI/litellm/pull/15441)
- **[Anthropic](../../docs/providers/anthropic)**
- Reduce claude-4-sonnet max_output_tokens to 64k - [PR #15409](https://github.com/BerriAI/litellm/pull/15409)
- Added claude-haiku-4.5 - [PR #15579](https://github.com/BerriAI/litellm/pull/15579)
- Add support for thinking blocks and redacted thinking blocks in Anthropic v1/messages API - [PR #15501](https://github.com/BerriAI/litellm/pull/15501)
- **[Bedrock](../../docs/providers/bedrock)**
- Add anthropic.claude-haiku-4-5-20251001-v1:0 on Bedrock, VertexAI - [PR #15581](https://github.com/BerriAI/litellm/pull/15581)
- Add Claude Haiku 4.5 support for Bedrock global and US regions - [PR #15650](https://github.com/BerriAI/litellm/pull/15650)
- Add Claude Haiku 4.5 support for Bedrock Other regions - [PR #15653](https://github.com/BerriAI/litellm/pull/15653)
- Add JP Cross-Region Inference jp.anthropic.claude-haiku-4-5-20251001 - [PR #15598](https://github.com/BerriAI/litellm/pull/15598)
- Fix: bedrock-pricing-geo-inregion-cross-region / add Global Cross-Region Inference - [PR #15685](https://github.com/BerriAI/litellm/pull/15685)
- Fix: Support us-gov prefix for AWS GovCloud Bedrock models - [PR #15626](https://github.com/BerriAI/litellm/pull/15626)
- Fix GPT-OSS in Bedrock now supports streaming. Revert fake streaming - [PR #15668](https://github.com/BerriAI/litellm/pull/15668)
- **[Gemini](../../docs/providers/gemini)**
- Feat(pricing): Add Gemini 2.5 Flash Image (Nano Banana) in GA - [PR #15557](https://github.com/BerriAI/litellm/pull/15557)
- Fix: Gemini 2.5 Flash Image should not have supports_web_search=true - [PR #15642](https://github.com/BerriAI/litellm/pull/15642)
- Remove penalty params as supported params for gemini preview model - [PR #15503](https://github.com/BerriAI/litellm/pull/15503)
- **[Ollama](../../docs/providers/ollama)**
- Fix(ollama/chat): correctly map reasoning_effort to think in requests - [PR #15465](https://github.com/BerriAI/litellm/pull/15465)
- **[OpenRouter](../../docs/providers/openrouter)**
- Add anthropic/claude-sonnet-4.5 to OpenRouter cost map - [PR #15472](https://github.com/BerriAI/litellm/pull/15472)
- Prompt caching for anthropic models with OpenRouter - [PR #15535](https://github.com/BerriAI/litellm/pull/15535)
- Get completion cost directly from OpenRouter - [PR #15448](https://github.com/BerriAI/litellm/pull/15448)
- Fix OpenRouter Claude Opus 4 model naming - [PR #15495](https://github.com/BerriAI/litellm/pull/15495)
- **[CometAPI](../../docs/providers/comet)**
- Fix(cometapi): improve CometAPI provider support (embeddings, image generation, docs) - [PR #15591](https://github.com/BerriAI/litellm/pull/15591)
- **[Lemonade](../../docs/providers/lemonade)**
- Adding new models to the lemonade provider - [PR #15554](https://github.com/BerriAI/litellm/pull/15554)
- **[Watson X](../../docs/providers/watsonx)**
- Fix (pricing): Fix pricing for watsonx model family for various models - [PR #15670](https://github.com/BerriAI/litellm/pull/15670)
- **[Vercel AI Gateway](../../docs/providers/vercel_ai_gateway)**
- Add glm-4.6 model to pricing configuration - [PR #15679](https://github.com/BerriAI/litellm/pull/15679)
- **[Vertex AI](../../docs/providers/vertex)**
- Add Vertex AI Discovery Engine Rerank Support - [PR #15532](https://github.com/BerriAI/litellm/pull/15532)
### Bug Fixes
- **[Anthropic](../../docs/providers/anthropic)**
- Fix: Pricing for Claude Sonnet 4.5 in US regions is 10x too high - [PR #15374](https://github.com/BerriAI/litellm/pull/15374)
- **[OpenRouter](../../docs/providers/openrouter)**
- Change gpt-5-codex support in model_price json - [PR #15540](https://github.com/BerriAI/litellm/pull/15540)
- **[Bedrock](../../docs/providers/bedrock)**
- Fix filtering headers for signature calcs - [PR #15590](https://github.com/BerriAI/litellm/pull/15590)
- **General**
- Add native reasoning and streaming support flag for gpt-5-codex - [PR #15569](https://github.com/BerriAI/litellm/pull/15569)
---
## LLM API Endpoints
#### Features
- **[Responses API](../../docs/response_api)**
- Responses API - enable calling anthropic/gemini models in Responses API streaming in openai ruby sdk + DB - sanity check pending migrations before startup - [PR #15432](https://github.com/BerriAI/litellm/pull/15432)
- Add support for responses mode in health check - [PR #15658](https://github.com/BerriAI/litellm/pull/15658)
- **[OCR API](../../docs/ocr)**
- Feat: Add native litellm.ocr() functions - [PR #15567](https://github.com/BerriAI/litellm/pull/15567)
- Feat: Add /ocr route on LiteLLM AI Gateway - Adds support for native Mistral OCR calling - [PR #15571](https://github.com/BerriAI/litellm/pull/15571)
- Feat: Add Azure AI Mistral OCR Integration - [PR #15572](https://github.com/BerriAI/litellm/pull/15572)
- Feat: Native /ocr endpoint support - [PR #15573](https://github.com/BerriAI/litellm/pull/15573)
- Feat: Add Cost Tracking for /ocr endpoints - [PR #15678](https://github.com/BerriAI/litellm/pull/15678)
- **[/generateContent](../../docs/providers/gemini)**
- Fix: GEMINI - CLI - add google_routes to llm_api_routes - [PR #15500](https://github.com/BerriAI/litellm/pull/15500)
- Fix Pydantic validation error for citationMetadata.citationSources in Google GenAI responses - [PR #15592](https://github.com/BerriAI/litellm/pull/15592)
- **[Images API](../../docs/image_generation)**
- Fix: Dall-e-2 for Image Edits API - [PR #15604](https://github.com/BerriAI/litellm/pull/15604)
- **[Bedrock Passthrough](../../docs/pass_through/bedrock)**
- Feat: Allow calling /invoke, /converse routes through AI Gateway + models on config.yaml - [PR #15618](https://github.com/BerriAI/litellm/pull/15618)
#### Bugs
- **General**
- Fix: Convert object to a correct type - [PR #15634](https://github.com/BerriAI/litellm/pull/15634)
- Bug Fix: Tags as metadata dicts were raising exceptions - [PR #15625](https://github.com/BerriAI/litellm/pull/15625)
- Add type hint to function_to_dict and fix typo - [PR #15580](https://github.com/BerriAI/litellm/pull/15580)
---
## Management Endpoints / UI
#### Features
- **Virtual Keys**
- Docs: Key Rotations - [PR #15455](https://github.com/BerriAI/litellm/pull/15455)
- Fix: UI - Key Max Budget Removal Error Fix - [PR #15672](https://github.com/BerriAI/litellm/pull/15672)
- litellm_Key Settings Max Budget Removal Error Fix - [PR #15669](https://github.com/BerriAI/litellm/pull/15669)
- **Teams**
- Feat: Allow Team Admins to export a report of the team spending - [PR #15542](https://github.com/BerriAI/litellm/pull/15542)
- **Passthrough**
- Feat: Passthrough - allow admin to give access to specific passthrough endpoints - [PR #15401](https://github.com/BerriAI/litellm/pull/15401)
- **SCIM v2**
- Feat(scim_v2.py): if group.id doesn't exist, use external id + Passthrough - ensure updates and deletions persist across instances - [PR #15276](https://github.com/BerriAI/litellm/pull/15276)
- **SSO**
- Feat: UI SSO - Add PKCE for OKTA SSO - [PR #15608](https://github.com/BerriAI/litellm/pull/15608)
- Fix: Separate OAuth M2M authentication from UI SSO + Handle Introspection endpoint for Oauth2 - [PR #15667](https://github.com/BerriAI/litellm/pull/15667)
- Fix/entraid app roles jwt claim clean - [PR #15583](https://github.com/BerriAI/litellm/pull/15583)
---
## Logging / Guardrail / Prompt Management Integrations
#### Guardrails
- **General**
- Fix apply_guardrail endpoint returning raw string instead of ApplyGuardrailResponse - [PR #15436](https://github.com/BerriAI/litellm/pull/15436)
- Fix: Ensure guardrail memory sync after database updates - [PR #15633](https://github.com/BerriAI/litellm/pull/15633)
- Feat: add guardrail for image generation - [PR #15619](https://github.com/BerriAI/litellm/pull/15619)
- Feat: Add Guardrails for /v1/messages and /v1/responses API - [PR #15686](https://github.com/BerriAI/litellm/pull/15686)
- **[Pillar Security](../../docs/proxy/guardrails)**
- Feature: update pillar security integration to support no persistence mode in litellm proxy - [PR #15599](https://github.com/BerriAI/litellm/pull/15599)
#### Prompt Management
- **General**
- Small fix code snippet custom_prompt_management.md - [PR #15544](https://github.com/BerriAI/litellm/pull/15544)
---
## Spend Tracking, Budgets and Rate Limiting
- **Cost Tracking**
- Feat: Cost Tracking - specify a global vendor discount for costs - [PR #15546](https://github.com/BerriAI/litellm/pull/15546)
- Feat: UI - Allow setting Provider Discounts on UI - [PR #15550](https://github.com/BerriAI/litellm/pull/15550)
- **Budgets**
- Fix: improve budget clarity - [PR #15682](https://github.com/BerriAI/litellm/pull/15682)
---
## Performance / Loadbalancing / Reliability improvements
- **Router Optimizations**
- Perf(router): use shallow copy instead of deepcopy for model aliases - 10-100x faster than deepcopy on nested dict structures - [PR #15576](https://github.com/BerriAI/litellm/pull/15576)
- Perf(router): optimize string concatenation in hash generation - Improves time complexity from O(n²) to O(n) - [PR #15575](https://github.com/BerriAI/litellm/pull/15575)
- Perf(router): optimize model lookups with O(1) data structures - Replace O(n) scans with index map lookups - [PR #15578](https://github.com/BerriAI/litellm/pull/15578)
- Perf(router): optimize model lookups with O(1) index maps - Use model_id_to_deployment_index_map and model_name_to_deployment_indices for instant lookups - [PR #15574](https://github.com/BerriAI/litellm/pull/15574)
- Perf(router): optimize timing functions in completion hot path - Use time.perf_counter() for duration measurements and time.monotonic() for timeout calculations, providing 30-40% faster timing calls - [PR #15617](https://github.com/BerriAI/litellm/pull/15617)
- **SSL/TLS Performance**
- Feat(ssl): add configurable ECDH curve for TLS performance - Configure via ssl_ecdh_curve setting to disable PQC on OpenSSL 3.x for better performance - [PR #15617](https://github.com/BerriAI/litellm/pull/15617)
- **Token Counter**
- Fix(token-counter): extract model_info from deployment for custom_tokenizer - [PR #15680](https://github.com/BerriAI/litellm/pull/15680)
- **Performance Metrics**
- Add: perf summary - [PR #15458](https://github.com/BerriAI/litellm/pull/15458)
- **CI/CD**
- Fix: CI/CD - Missing env key & Linter type error - [PR #15606](https://github.com/BerriAI/litellm/pull/15606)
---
## Documentation Updates
- **Provider Documentation**
- Litellm docs 10 11 2025 - [PR #15457](https://github.com/BerriAI/litellm/pull/15457)
- Docs: add ecs deployment guide - [PR #15468](https://github.com/BerriAI/litellm/pull/15468)
- Docs: Update benchmark results - [PR #15461](https://github.com/BerriAI/litellm/pull/15461)
- Fix: add missing context to benchmark docs - [PR #15688](https://github.com/BerriAI/litellm/pull/15688)
- **General**
- Fixed a few typos - [PR #15267](https://github.com/BerriAI/litellm/pull/15267)
---
## New Contributors
* @jlan-nl made their first contribution in [PR #15374](https://github.com/BerriAI/litellm/pull/15374)
* @ImadSaddik made their first contribution in [PR #15267](https://github.com/BerriAI/litellm/pull/15267)
* @huangyafei made their first contribution in [PR #15472](https://github.com/BerriAI/litellm/pull/15472)
* @mubashir1osmani made their first contribution in [PR #15468](https://github.com/BerriAI/litellm/pull/15468)
* @kowyo made their first contribution in [PR #15465](https://github.com/BerriAI/litellm/pull/15465)
* @dhruvyad made their first contribution in [PR #15448](https://github.com/BerriAI/litellm/pull/15448)
* @davizucon made their first contribution in [PR #15544](https://github.com/BerriAI/litellm/pull/15544)
* @FelipeRodriguesGare made their first contribution in [PR #15540](https://github.com/BerriAI/litellm/pull/15540)
* @ndrsfel made their first contribution in [PR #15557](https://github.com/BerriAI/litellm/pull/15557)
* @shinharaguchi made their first contribution in [PR #15598](https://github.com/BerriAI/litellm/pull/15598)
* @TensorNull made their first contribution in [PR #15591](https://github.com/BerriAI/litellm/pull/15591)
* @TeddyAmkie made their first contribution in [PR #15583](https://github.com/BerriAI/litellm/pull/15583)
* @aniketmaurya made their first contribution in [PR #15580](https://github.com/BerriAI/litellm/pull/15580)
* @eddierichter-amd made their first contribution in [PR #15554](https://github.com/BerriAI/litellm/pull/15554)
* @konekohana made their first contribution in [PR #15535](https://github.com/BerriAI/litellm/pull/15535)
* @Classic298 made their first contribution in [PR #15495](https://github.com/BerriAI/litellm/pull/15495)
* @afogel made their first contribution in [PR #15599](https://github.com/BerriAI/litellm/pull/15599)
* @orolega made their first contribution in [PR #15633](https://github.com/BerriAI/litellm/pull/15633)
* @LucasSugi made their first contribution in [PR #15634](https://github.com/BerriAI/litellm/pull/15634)
* @uc4w6c made their first contribution in [PR #15619](https://github.com/BerriAI/litellm/pull/15619)
* @Sameerlite made their first contribution in [PR #15658](https://github.com/BerriAI/litellm/pull/15658)
* @yuneng-jiang made their first contribution in [PR #15672](https://github.com/BerriAI/litellm/pull/15672)
* @Nikro made their first contribution in [PR #15680](https://github.com/BerriAI/litellm/pull/15680)
---
## Full Changelog
**[View complete changelog on GitHub](https://github.com/BerriAI/litellm/compare/v1.78.0-stable...v1.78.4-stable)**

View file

@ -418,6 +418,7 @@ const sidebars = {
"providers/azure/azure",
"providers/azure/azure_responses",
"providers/azure/azure_embedding",
"providers/azure/azure_speech",
]
},
{
@ -425,6 +426,7 @@ const sidebars = {
label: "Azure AI",
items: [
"providers/azure_ai",
"providers/azure_ai_speech",
"providers/azure_ai_img",
]
},

View file

@ -767,6 +767,9 @@ azure_llms = {
"gpt-35-turbo": "azure/gpt-35-turbo",
"gpt-35-turbo-16k": "azure/gpt-35-turbo-16k",
"gpt-35-turbo-instruct": "azure/gpt-35-turbo-instruct",
"azure/gpt-41":"gpt-4.1",
"azure/gpt-41-mini":"gpt-4.1-mini",
"azure/gpt-41-nano":"gpt-4.1-nano"
}
azure_embedding_models = {

View file

@ -19,6 +19,7 @@ if TYPE_CHECKING:
import litellm
from litellm._logging import print_verbose, verbose_logger
from litellm.constants import DEFAULT_MAX_REDIS_BATCH_CACHE_SIZE
from .base_cache import BaseCache
from .in_memory_cache import InMemoryCache
@ -60,7 +61,7 @@ class DualCache(BaseCache):
default_in_memory_ttl: Optional[float] = None,
default_redis_ttl: Optional[float] = None,
default_redis_batch_cache_expiry: Optional[float] = None,
default_max_redis_batch_cache_size: int = 100,
default_max_redis_batch_cache_size: int = DEFAULT_MAX_REDIS_BATCH_CACHE_SIZE,
) -> None:
super().__init__()
# If in_memory_cache is not provided, use the default InMemoryCache
@ -260,7 +261,7 @@ class DualCache(BaseCache):
**kwargs,
):
try:
result = [None for _ in range(len(keys))]
result = [None] * len(keys)
if self.in_memory_cache is not None:
in_memory_result = await self.in_memory_cache.async_batch_get_cache(
keys, **kwargs
@ -283,20 +284,27 @@ class DualCache(BaseCache):
redis_result = await self.redis_cache.async_batch_get_cache(
sublist_keys, parent_otel_span=parent_otel_span
)
# Update the last access time for ALL queried keys
# This includes keys with None values to throttle repeated Redis queries
for key in sublist_keys:
self.last_redis_batch_access_time[key] = current_time
# Short-circuit if redis_result is None or contains only None values
if redis_result is None or all(v is None for v in redis_result.values()):
return result
if redis_result is not None:
# Update in-memory cache with the value from Redis
for key, value in redis_result.items():
if value is not None:
await self.in_memory_cache.async_set_cache(
key, redis_result[key], **kwargs
)
# Update the last access time for each key fetched from Redis
self.last_redis_batch_access_time[key] = current_time
# Pre-compute key-to-index mapping for O(1) lookup
key_to_index = {key: i for i, key in enumerate(keys)}
# Update both result and in-memory cache in a single loop
for key, value in redis_result.items():
index = keys.index(key)
result[index] = value
result[key_to_index[key]] = value
if value is not None and self.in_memory_cache is not None:
await self.in_memory_cache.async_set_cache(
key, value, **kwargs
)
return result
except Exception:

View file

@ -93,6 +93,14 @@ AIOHTTP_CONNECTOR_LIMIT = int(os.getenv("AIOHTTP_CONNECTOR_LIMIT", 0))
AIOHTTP_KEEPALIVE_TIMEOUT = int(os.getenv("AIOHTTP_KEEPALIVE_TIMEOUT", 120))
AIOHTTP_TTL_DNS_CACHE = int(os.getenv("AIOHTTP_TTL_DNS_CACHE", 300))
# WebSocket constants
# Default to None (unlimited) to match OpenAI's official agents SDK behavior
# https://github.com/openai/openai-agents-python/blob/cf1b933660e44fd37b4350c41febab8221801409/src/agents/realtime/openai_realtime.py#L235
_max_size_env = os.getenv("REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES")
REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES = (
int(_max_size_env) if _max_size_env is not None else None
)
# SSL/TLS cipher configuration for faster handshakes
# Strategy: Strongly prefer fast modern ciphers, but allow fallback to commonly supported ones
# This balances performance with broad compatibility
@ -199,6 +207,9 @@ JITTER = float(os.getenv("JITTER", 0.75))
DEFAULT_IN_MEMORY_TTL = int(
os.getenv("DEFAULT_IN_MEMORY_TTL", 5)
) # default time to live for the in-memory cache
DEFAULT_MAX_REDIS_BATCH_CACHE_SIZE = int(
os.getenv("DEFAULT_MAX_REDIS_BATCH_CACHE_SIZE", 1000)
) # default max size for redis batch cache
DEFAULT_POLLING_INTERVAL = float(
os.getenv("DEFAULT_POLLING_INTERVAL", 0.03)
) # default polling interval for the scheduler
@ -396,6 +407,7 @@ OPENAI_CHAT_COMPLETION_PARAMS = [
"extra_headers",
"thinking",
"web_search_options",
"service_tier",
]
OPENAI_TRANSCRIPTION_PARAMS = [
@ -450,6 +462,7 @@ DEFAULT_CHAT_COMPLETION_PARAM_VALUES = {
"reasoning_effort": None,
"thinking": None,
"web_search_options": None,
"service_tier": None,
"safety_identifier": None,
}
@ -970,6 +983,10 @@ DEFAULT_SOFT_BUDGET = float(
# makes it clear this is a rate limit error for a litellm virtual key
RATE_LIMIT_ERROR_MESSAGE_FOR_VIRTUAL_KEY = "LiteLLM Virtual Key user_api_key_hash"
# Python garbage collection threshold configuration
# Format: "gen0,gen1,gen2" e.g., "1000,50,50"
PYTHON_GC_THRESHOLD = os.getenv("PYTHON_GC_THRESHOLD")
# pass through route constansts
BEDROCK_AGENT_RUNTIME_PASS_THROUGH_ROUTES = [
"agents/",

View file

@ -120,17 +120,18 @@ class AnthropicCacheControlHook(CustomPromptManagement):
- list of objects
This method handles inserting cache control in both cases.
Per Anthropic's API specification, when using multiple content blocks,
only the last content block can have cache_control.
"""
message_content = message.get("content", None)
# 1. if string, insert cache control in the message
if isinstance(message_content, str):
message["cache_control"] = control # type: ignore
# 2. list of objects
# 2. list of objects - only apply to last item per Anthropic spec
elif isinstance(message_content, list):
for content_item in message_content:
if isinstance(content_item, dict):
content_item["cache_control"] = control # type: ignore
if len(message_content) > 0 and isinstance(message_content[-1], dict):
message_content[-1]["cache_control"] = control # type: ignore
return message
@property

View file

@ -100,6 +100,11 @@ class HeliconeLogger:
for header_key in proxy_headers:
if header_key.startswith("helicone_"):
metadata[header_key] = proxy_headers.get(header_key)
# Remove OpenTelemetry span from metadata as it's not JSON serializable
# The span is used internally for tracing but shouldn't be logged to external services
if "litellm_parent_otel_span" in metadata:
metadata.pop("litellm_parent_otel_span")
return metadata

View file

@ -247,12 +247,12 @@ class OpenTelemetry(CustomLogger):
metrics.set_meter_provider(meter_provider)
self._operation_duration_histogram = meter.create_histogram(
name="gen_ai.client.operation.duration", # Replace with semconv constant in otel 1.38
name="gen_ai.client.operation.duration", # Replace with semconv constant in otel 1.38
description="GenAI operation duration",
unit="s",
)
self._token_usage_histogram = meter.create_histogram(
name="gen_ai.client.token.usage", # Replace with semconv constant in otel 1.38
name="gen_ai.client.token.usage", # Replace with semconv constant in otel 1.38
description="GenAI token usage",
unit="{token}",
)
@ -480,9 +480,9 @@ class OpenTelemetry(CustomLogger):
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")
standard_callback_dynamic_params: Optional[StandardCallbackDynamicParams] = (
kwargs.get("standard_callback_dynamic_params")
)
if not standard_callback_dynamic_params:
return None
@ -543,7 +543,7 @@ class OpenTelemetry(CustomLogger):
# 4. Metrics & cost recording
self._record_metrics(kwargs, response_obj, start_time, end_time)
# 5. Semantic logs.
# 5. Semantic logs.
if self.config.enable_events:
self._emit_semantic_logs(kwargs, response_obj, span)
@ -581,7 +581,6 @@ class OpenTelemetry(CustomLogger):
raw_span_name = generation_name if generation_name else RAW_REQUEST_SPAN_NAME
otel_tracer: Tracer = self.get_tracer_to_use_for_request(kwargs)
raw_span = otel_tracer.start_span(
name=raw_span_name,
@ -626,6 +625,13 @@ class OpenTelemetry(CustomLogger):
if md.get(key) is not None:
common_attrs[f"metadata.{key}"] = str(md[key])
# get hidden params
hidden_params = getattr(std_log, "hidden_params", None) or (std_log or {}).get(
"hidden_params", {}
)
if hidden_params:
common_attrs["hidden_params"] = safe_dumps(hidden_params)
if self._operation_duration_histogram:
self._operation_duration_histogram.record(
duration_s, attributes=common_attrs
@ -653,6 +659,7 @@ class OpenTelemetry(CustomLogger):
return
from opentelemetry._logs import LogRecord, get_logger
otel_logger = get_logger(LITELLM_LOGGER_NAME)
parent_ctx = span.get_span_context()
@ -708,7 +715,6 @@ class OpenTelemetry(CustomLogger):
)
)
def _create_guardrail_span(
self, kwargs: Optional[dict], context: Optional[Context]
):
@ -920,6 +926,14 @@ class OpenTelemetry(CustomLogger):
span=span, key="metadata.{}".format(key), value=value
)
# get hidden params
hidden_params = getattr(
standard_logging_payload, "hidden_params", None
) or (standard_logging_payload or {}).get("hidden_params", {})
if hidden_params:
self.safe_set_attribute(
span=span, key="hidden_params", value=safe_dumps(hidden_params)
)
#############################################
########## LLM Request Attributes ###########
#############################################

View file

@ -10,14 +10,20 @@ class ProviderSpecificHeaderUtils:
custom_llm_provider: Optional[str],
) -> Dict:
"""
Get the provider specific headers for the given custom llm provider
Get the provider specific headers for the given custom llm provider.
Supports comma-separated provider lists for headers that work across multiple providers.
Returns:
Optional[Dict]: The provider specific headers for the given custom llm provider
Dict: The provider specific headers for the given custom llm provider
"""
if (
provider_specific_header is not None
and provider_specific_header.get("custom_llm_provider") == custom_llm_provider
):
if provider_specific_header is None or custom_llm_provider is None:
return {}
stored_providers = provider_specific_header.get("custom_llm_provider", "")
provider_list = [p.strip() for p in stored_providers.split(",")]
if custom_llm_provider in provider_list:
return provider_specific_header.get("extra_headers", {})
return {}
return {}

View file

@ -364,17 +364,19 @@ def phind_codellama_pt(messages):
return prompt
def _render_chat_template(env, chat_template: str, bos_token: str, eos_token: str, messages: list) -> str:
def _render_chat_template(
env, chat_template: str, bos_token: str, eos_token: str, messages: list
) -> str:
"""
Shared template rendering logic for both sync and async hf_chat_template
Args:
env: Jinja2 environment
chat_template: Chat template string
bos_token: Beginning of sequence token
eos_token: End of sequence token
messages: Messages to render
Returns:
Rendered template string
"""
@ -456,7 +458,7 @@ async def _afetch_and_extract_template(
) -> Tuple[str, str, str]:
"""
Async version: Fetch template and tokens from HuggingFace.
Returns: (chat_template, bos_token, eos_token)
"""
from litellm.litellm_core_utils.prompt_templates.huggingface_template_handler import (
@ -518,7 +520,7 @@ def _fetch_and_extract_template(
) -> Tuple[str, str, str]:
"""
Sync version: Fetch template and tokens from HuggingFace.
Returns: (chat_template, bos_token, eos_token)
"""
from litellm.litellm_core_utils.prompt_templates.huggingface_template_handler import (
@ -604,9 +606,7 @@ async def ahf_chat_template(
)
def hf_chat_template(
model: str, messages: list, chat_template: Optional[Any] = None
):
def hf_chat_template(model: str, messages: list, chat_template: Optional[Any] = None):
"""HuggingFace chat template (sync version)"""
from litellm.litellm_core_utils.prompt_templates.huggingface_template_handler import (
_get_chat_template_file,
@ -1205,10 +1205,10 @@ def convert_to_gemini_tool_call_invoke(
if tool_calls is not None:
for tool in tool_calls:
if "function" in tool:
gemini_function_call: Optional[
VertexFunctionCall
] = _gemini_tool_call_invoke_helper(
function_call_params=tool["function"]
gemini_function_call: Optional[VertexFunctionCall] = (
_gemini_tool_call_invoke_helper(
function_call_params=tool["function"]
)
)
if gemini_function_call is not None:
_parts_list.append(
@ -1727,9 +1727,9 @@ def anthropic_messages_pt( # noqa: PLR0915
)
if "cache_control" in _content_element:
_anthropic_content_element[
"cache_control"
] = _content_element["cache_control"]
_anthropic_content_element["cache_control"] = (
_content_element["cache_control"]
)
user_content.append(_anthropic_content_element)
elif m.get("type", "") == "text":
m = cast(ChatCompletionTextObject, m)
@ -1767,9 +1767,9 @@ def anthropic_messages_pt( # noqa: PLR0915
)
if "cache_control" in _content_element:
_anthropic_content_text_element[
"cache_control"
] = _content_element["cache_control"]
_anthropic_content_text_element["cache_control"] = (
_content_element["cache_control"]
)
user_content.append(_anthropic_content_text_element)
@ -3964,9 +3964,11 @@ def _bedrock_tools_pt(tools: List) -> List[BedrockToolBlock]:
# related issue: https://github.com/BerriAI/litellm/issues/5007
# Bedrock tool names must satisfy regular expression pattern: [a-zA-Z][a-zA-Z0-9_]* ensure this is true
name = make_valid_bedrock_tool_name(input_tool_name=name)
description = tool.get("function", {}).get(
"description", name
) # converse api requires a description
_tool_description = tool.get("function", {}).get("description", None)
if _tool_description: # bedrock doesn't accept empty "" or None descriptions
description = _tool_description
else:
description = name
defs = parameters.pop("$defs", {})
defs_copy = copy.deepcopy(defs)
@ -4171,8 +4173,11 @@ def prompt_factory(
return azure_text_pt(messages=messages)
elif custom_llm_provider == "watsonx":
from litellm.llms.watsonx.chat.transformation import IBMWatsonXChatConfig
return IBMWatsonXChatConfig.apply_prompt_template(model=model, messages=messages)
return IBMWatsonXChatConfig.apply_prompt_template(
model=model, messages=messages
)
try:
if "meta-llama/llama-2" in model and "chat" in model:
return llama_2_chat_pt(messages=messages)

View file

@ -6,6 +6,8 @@ This requires websockets, and is currently only supported on LiteLLM Proxy.
from typing import Any, Optional, cast
from litellm.constants import REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES
from ....litellm_core_utils.litellm_logging import Logging as LiteLLMLogging
from ....litellm_core_utils.realtime_streaming import RealTimeStreaming
from ..azure import AzureChatCompletion
@ -64,6 +66,7 @@ class AzureOpenAIRealtime(AzureChatCompletion):
extra_headers={
"api-key": api_key, # type: ignore
},
max_size=REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES,
) as backend_ws:
realtime_streaming = RealTimeStreaming(
websocket, cast(ClientConnection, backend_ws), logging_obj

View file

@ -0,0 +1,8 @@
"""Azure Text-to-Speech module"""
from .transformation import AzureAVATextToSpeechConfig
__all__ = [
"AzureAVATextToSpeechConfig",
]

View file

@ -0,0 +1,373 @@
"""
Azure AVA (Cognitive Services) Text-to-Speech transformation
Maps OpenAI TTS spec to Azure Cognitive Services TTS API
"""
from typing import TYPE_CHECKING, Any, Coroutine, Dict, Optional, Union
from urllib.parse import urlparse
import httpx
import litellm
from litellm.llms.base_llm.text_to_speech.transformation import (
BaseTextToSpeechConfig,
TextToSpeechRequestData,
)
from litellm.secret_managers.main import get_secret_str
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
from litellm.types.llms.openai import HttpxBinaryResponseContent
else:
LiteLLMLoggingObj = Any
HttpxBinaryResponseContent = Any
class AzureAVATextToSpeechConfig(BaseTextToSpeechConfig):
"""
Configuration for Azure AVA (Cognitive Services) Text-to-Speech
Reference: https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech
"""
# Azure endpoint domains
COGNITIVE_SERVICES_DOMAIN = "api.cognitive.microsoft.com"
TTS_SPEECH_DOMAIN = "tts.speech.microsoft.com"
TTS_ENDPOINT_PATH = "/cognitiveservices/v1"
# Voice name mappings from OpenAI voices to Azure voices
VOICE_MAPPINGS = {
"alloy": "en-US-JennyNeural",
"echo": "en-US-GuyNeural",
"fable": "en-GB-RyanNeural",
"onyx": "en-US-DavisNeural",
"nova": "en-US-AmberNeural",
"shimmer": "en-US-AriaNeural",
}
# Response format mappings from OpenAI to Azure
FORMAT_MAPPINGS = {
"mp3": "audio-24khz-48kbitrate-mono-mp3",
"opus": "ogg-48khz-16bit-mono-opus",
"aac": "audio-24khz-48kbitrate-mono-mp3", # Azure doesn't have AAC, use MP3
"flac": "audio-24khz-48kbitrate-mono-mp3", # Azure doesn't have FLAC, use MP3
"wav": "riff-24khz-16bit-mono-pcm",
"pcm": "raw-24khz-16bit-mono-pcm",
}
def dispatch_text_to_speech(
self,
model: str,
input: str,
voice: Optional[Union[str, Dict]],
optional_params: Dict,
litellm_params_dict: Dict,
logging_obj: "LiteLLMLoggingObj",
timeout: Union[float, httpx.Timeout],
extra_headers: Optional[Dict[str, Any]],
base_llm_http_handler: Any,
aspeech: bool,
api_base: Optional[str],
api_key: Optional[str],
**kwargs: Any,
) -> Union[
"HttpxBinaryResponseContent",
Coroutine[Any, Any, "HttpxBinaryResponseContent"],
]:
"""
Dispatch method to handle Azure AVA TTS requests
This method encapsulates Azure-specific credential resolution and parameter handling
Args:
base_llm_http_handler: The BaseLLMHTTPHandler instance from main.py
"""
# Resolve api_base from multiple sources
api_base = (
api_base
or litellm_params_dict.get("api_base")
or litellm.api_base
or get_secret_str("AZURE_API_BASE")
)
# Resolve api_key from multiple sources (Azure-specific)
api_key = (
api_key
or litellm_params_dict.get("api_key")
or litellm.api_key
or litellm.azure_key
or get_secret_str("AZURE_OPENAI_API_KEY")
or get_secret_str("AZURE_API_KEY")
)
# Convert voice to string if it's a dict (for Azure AVA, voice must be a string)
voice_str: Optional[str] = None
if isinstance(voice, str):
voice_str = voice
elif isinstance(voice, dict):
# Extract voice name from dict if needed
voice_str = voice.get("name") if voice else None
litellm_params_dict.update({
"api_key": api_key,
"api_base": api_base,
})
# Call the text_to_speech_handler
response = base_llm_http_handler.text_to_speech_handler(
model=model,
input=input,
voice=voice_str,
text_to_speech_provider_config=self,
text_to_speech_optional_params=optional_params,
custom_llm_provider="azure",
litellm_params=litellm_params_dict,
logging_obj=logging_obj,
timeout=timeout,
extra_headers=extra_headers,
client=None,
_is_async=aspeech,
)
return response
def get_supported_openai_params(self, model: str) -> list:
"""
Azure AVA TTS supports these OpenAI parameters
"""
return ["voice", "response_format", "speed"]
def _convert_speed_to_azure_rate(self, speed: float) -> str:
"""
Convert OpenAI speed value to Azure SSML prosody rate percentage
Args:
speed: OpenAI speed value (0.25-4.0, default 1.0)
Returns:
Azure rate string with percentage (e.g., "+50%", "-50%", "+0%")
Examples:
speed=1.0 -> "+0%" (default)
speed=2.0 -> "+100%"
speed=0.5 -> "-50%"
"""
rate_percentage = int((speed - 1.0) * 100)
return f"{rate_percentage:+d}%"
def map_openai_params(
self,
model: str,
optional_params: Dict,
drop_params: bool,
) -> Dict:
"""
Map OpenAI parameters to Azure AVA TTS parameters
"""
mapped_params = {}
# Map voice
if "voice" in optional_params:
voice = optional_params["voice"]
# If it's already an Azure voice, use it directly
if isinstance(voice, str):
if voice in self.VOICE_MAPPINGS:
mapped_params["voice"] = self.VOICE_MAPPINGS[voice]
else:
# Assume it's already an Azure voice name
mapped_params["voice"] = voice
# Map response format
if "response_format" in optional_params:
format_name = optional_params["response_format"]
if format_name in self.FORMAT_MAPPINGS:
mapped_params["output_format"] = self.FORMAT_MAPPINGS[format_name]
else:
# Try to use it directly as Azure format
mapped_params["output_format"] = format_name
else:
# Default to MP3
mapped_params["output_format"] = "audio-24khz-48kbitrate-mono-mp3"
# Map speed (OpenAI: 0.25-4.0, Azure: prosody rate)
if "speed" in optional_params:
speed = optional_params["speed"]
if speed is not None:
mapped_params["rate"] = self._convert_speed_to_azure_rate(speed=speed)
return mapped_params
def validate_environment(
self,
headers: dict,
model: str,
api_key: Optional[str] = None,
api_base: Optional[str] = None,
) -> dict:
"""
Validate Azure environment and set up authentication headers
"""
validated_headers = headers.copy()
# Azure AVA TTS requires either:
# 1. Ocp-Apim-Subscription-Key header, or
# 2. Authorization: Bearer <token> header
# We'll use the token-based auth via our token handler
# The token will be added later in the handler
if api_key:
# If subscription key is provided, use it directly
validated_headers["Ocp-Apim-Subscription-Key"] = api_key
# Content-Type for SSML
validated_headers["Content-Type"] = "application/ssml+xml"
# User-Agent
validated_headers["User-Agent"] = "litellm"
return validated_headers
def get_complete_url(
self,
model: str,
api_base: Optional[str],
litellm_params: dict,
) -> str:
"""
Get the complete URL for Azure AVA TTS request
Azure TTS endpoint format:
https://{region}.tts.speech.microsoft.com/cognitiveservices/v1
"""
if api_base is None:
raise ValueError(
f"api_base is required for Azure AVA TTS. "
f"Format: https://{{region}}.{self.COGNITIVE_SERVICES_DOMAIN} or "
f"https://{{region}}.{self.TTS_SPEECH_DOMAIN}"
)
# Remove trailing slash and parse URL
api_base = api_base.rstrip("/")
parsed_url = urlparse(api_base)
hostname = parsed_url.hostname or ""
# Check if it's a Cognitive Services endpoint (convert to TTS endpoint)
if self._is_cognitive_services_endpoint(hostname=hostname):
region = self._extract_region_from_hostname(
hostname=hostname,
domain=self.COGNITIVE_SERVICES_DOMAIN
)
return self._build_tts_url(region=region)
# Check if it's already a TTS endpoint
if self._is_tts_endpoint(hostname=hostname):
if not api_base.endswith(self.TTS_ENDPOINT_PATH):
return f"{api_base}{self.TTS_ENDPOINT_PATH}"
return api_base
# Assume it's a custom endpoint, append the path
return f"{api_base}{self.TTS_ENDPOINT_PATH}"
def _is_cognitive_services_endpoint(self, hostname: str) -> bool:
"""Check if hostname is a Cognitive Services endpoint"""
return (
hostname == self.COGNITIVE_SERVICES_DOMAIN
or hostname.endswith(f".{self.COGNITIVE_SERVICES_DOMAIN}")
)
def _is_tts_endpoint(self, hostname: str) -> bool:
"""Check if hostname is a TTS endpoint"""
return (
hostname == self.TTS_SPEECH_DOMAIN
or hostname.endswith(f".{self.TTS_SPEECH_DOMAIN}")
)
def _extract_region_from_hostname(self, hostname: str, domain: str) -> str:
"""
Extract region from hostname
Examples:
eastus.api.cognitive.microsoft.com -> eastus
api.cognitive.microsoft.com -> ""
"""
if hostname.endswith(f".{domain}"):
return hostname[:-len(f".{domain}")]
return ""
def _build_tts_url(self, region: str) -> str:
"""Build the complete TTS URL with region"""
if region:
return f"https://{region}.{self.TTS_SPEECH_DOMAIN}{self.TTS_ENDPOINT_PATH}"
return f"https://{self.TTS_SPEECH_DOMAIN}{self.TTS_ENDPOINT_PATH}"
def transform_text_to_speech_request(
self,
model: str,
input: str,
voice: Optional[str],
optional_params: Dict,
litellm_params: Dict,
headers: dict,
) -> TextToSpeechRequestData:
"""
Transform OpenAI TTS request to Azure AVA TTS SSML format
Note: optional_params should already be mapped via map_openai_params in main.py
Returns:
TextToSpeechRequestData: Contains SSML body and Azure-specific headers
"""
# Get voice (already mapped in main.py, or use default)
azure_voice = optional_params.get("voice", "en-US-AriaNeural")
# Get output format (already mapped in main.py)
output_format = optional_params.get(
"output_format", "audio-24khz-48kbitrate-mono-mp3"
)
headers["X-Microsoft-OutputFormat"] = output_format
# Build SSML
rate = optional_params.get("rate", "0%")
# Escape XML special characters in input text
escaped_input = (
input.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;")
.replace('"', "&quot;")
.replace("'", "&apos;")
)
ssml_body = f"""
<speak version='1.0' xml:lang='en-US'>
<voice name='{azure_voice}'>
<prosody rate='{rate}'>
{escaped_input}
</prosody>
</voice>
</speak>
"""
return {
"ssml_body": ssml_body,
"headers": headers,
}
def transform_text_to_speech_response(
self,
model: str,
raw_response: httpx.Response,
logging_obj: "LiteLLMLoggingObj",
) -> "HttpxBinaryResponseContent":
"""
Transform Azure AVA TTS response to standard format
Azure returns the audio data directly in the response body
"""
from litellm.types.llms.openai import HttpxBinaryResponseContent
# Azure returns audio data directly in the response body
# Wrap it in HttpxBinaryResponseContent for consistent return type
return HttpxBinaryResponseContent(raw_response)

View file

@ -0,0 +1,147 @@
import types
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict
import httpx
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from litellm.types.llms.openai import (
HttpxBinaryResponseContent as _HttpxBinaryResponseContent,
)
from ..chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
HttpxBinaryResponseContent = _HttpxBinaryResponseContent
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
HttpxBinaryResponseContent = Any
class TextToSpeechRequestData(TypedDict, total=False):
"""
Structured return type for text-to-speech transformations.
This ensures a consistent interface across all TTS providers.
Providers should set ONE of: dict_body, ssml_body, or text_body.
"""
dict_body: Dict[str, Any] # JSON request body (e.g., OpenAI TTS)
ssml_body: str # SSML/XML string body (e.g., Azure AVA TTS)
headers: Dict[str, str] # Provider-specific headers to merge with base headers
class BaseTextToSpeechConfig(ABC):
def __init__(self):
pass
@classmethod
def get_config(cls):
return {
k: v
for k, v in cls.__dict__.items()
if not k.startswith("__")
and not k.startswith("_abc")
and not isinstance(
v,
(
types.FunctionType,
types.BuiltinFunctionType,
classmethod,
staticmethod,
),
)
and v is not None
}
@abstractmethod
def get_supported_openai_params(self, model: str) -> list:
"""
Get list of OpenAI TTS parameters supported by this provider
"""
pass
@abstractmethod
def map_openai_params(
self,
model: str,
optional_params: Dict,
drop_params: bool,
) -> Dict:
"""
Map OpenAI TTS parameters to provider-specific parameters
"""
pass
@abstractmethod
def validate_environment(
self,
headers: dict,
model: str,
api_key: Optional[str] = None,
api_base: Optional[str] = None,
) -> dict:
"""
Validate environment and return headers
"""
return {}
@abstractmethod
def get_complete_url(
self,
model: str,
api_base: Optional[str],
litellm_params: dict,
) -> str:
"""
Get the complete url for the request
"""
if api_base is None:
raise ValueError("api_base is required")
return api_base
@abstractmethod
def transform_text_to_speech_request(
self,
model: str,
input: str,
voice: Optional[str],
optional_params: Dict,
litellm_params: Dict,
headers: dict,
) -> TextToSpeechRequestData:
"""
Transform request to provider-specific format.
Returns:
TextToSpeechRequestData: A structured dict containing:
- body: The request body (JSON dict, XML string, or binary data)
- headers: Provider-specific headers to merge with base headers
"""
pass
@abstractmethod
def transform_text_to_speech_response(
self,
model: str,
raw_response: httpx.Response,
logging_obj: LiteLLMLoggingObj,
) -> "HttpxBinaryResponseContent":
"""
Transform provider response to standard format
"""
pass
def get_error_class(
self, error_message: str, status_code: int, headers: Dict
) -> BaseLLMException:
from ..chat.transformation import BaseLLMException
raise BaseLLMException(
status_code=status_code,
message=error_message,
headers=headers,
)

View file

@ -20,6 +20,7 @@ import litellm.litellm_core_utils
import litellm.types
import litellm.types.utils
from litellm._logging import verbose_logger
from litellm.constants import REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES
from litellm.litellm_core_utils.realtime_streaming import RealTimeStreaming
from litellm.llms.base_llm.anthropic_messages.transformation import (
BaseAnthropicMessagesConfig,
@ -43,6 +44,9 @@ from litellm.llms.base_llm.ocr.transformation import BaseOCRConfig, OCRResponse
from litellm.llms.base_llm.realtime.transformation import BaseRealtimeConfig
from litellm.llms.base_llm.rerank.transformation import BaseRerankConfig
from litellm.llms.base_llm.responses.transformation import BaseResponsesAPIConfig
from litellm.llms.base_llm.text_to_speech.transformation import (
BaseTextToSpeechConfig,
)
from litellm.llms.base_llm.vector_store.transformation import BaseVectorStoreConfig
from litellm.llms.custom_httpx.http_handler import (
AsyncHTTPHandler,
@ -62,6 +66,7 @@ from litellm.types.llms.anthropic_messages.anthropic_response import (
from litellm.types.llms.openai import (
CreateBatchRequest,
CreateFileRequest,
HttpxBinaryResponseContent,
OpenAIFileObject,
ResponseInputParam,
ResponsesAPIResponse,
@ -3280,6 +3285,7 @@ class BaseLLMHTTPHandler:
BaseAnthropicMessagesConfig,
BaseBatchesConfig,
BaseOCRConfig,
BaseTextToSpeechConfig,
"BasePassthroughConfig",
],
):
@ -3339,7 +3345,9 @@ class BaseLLMHTTPHandler:
try:
async with websockets.connect( # type: ignore
url, extra_headers=headers
url,
extra_headers=headers,
max_size=REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES,
) as backend_ws:
realtime_streaming = RealTimeStreaming(
websocket,
@ -4340,3 +4348,222 @@ class BaseLLMHTTPHandler:
raw_response=response,
logging_obj=logging_obj,
)
#####################################################################
################ TEXT TO SPEECH HANDLER ###########################
#####################################################################
def text_to_speech_handler(
self,
model: str,
input: str,
voice: Optional[str],
text_to_speech_provider_config: BaseTextToSpeechConfig,
text_to_speech_optional_params: Dict,
custom_llm_provider: str,
litellm_params: Dict,
logging_obj: LiteLLMLoggingObj,
timeout: Union[float, httpx.Timeout],
extra_headers: Optional[Dict[str, Any]] = None,
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
_is_async: bool = False,
) -> Union[
"HttpxBinaryResponseContent",
Coroutine[Any, Any, "HttpxBinaryResponseContent"],
]:
"""
Handles text-to-speech requests.
When _is_async=True, returns a coroutine instead of making the call directly.
"""
if _is_async:
return self.async_text_to_speech_handler(
model=model,
input=input,
voice=voice,
text_to_speech_provider_config=text_to_speech_provider_config,
text_to_speech_optional_params=text_to_speech_optional_params,
custom_llm_provider=custom_llm_provider,
litellm_params=litellm_params,
logging_obj=logging_obj,
extra_headers=extra_headers,
timeout=timeout,
client=client if isinstance(client, AsyncHTTPHandler) else None,
)
if client is None or not isinstance(client, HTTPHandler):
sync_httpx_client = _get_httpx_client(
params={"ssl_verify": litellm_params.get("ssl_verify", None)}
)
else:
sync_httpx_client = client
headers = text_to_speech_provider_config.validate_environment(
api_key=litellm_params.get("api_key"),
headers=extra_headers or {},
model=model,
api_base=litellm_params.get("api_base"),
)
if extra_headers:
headers.update(extra_headers)
api_base = text_to_speech_provider_config.get_complete_url(
model=model,
api_base=litellm_params.get("api_base"),
litellm_params=litellm_params,
)
request_data = text_to_speech_provider_config.transform_text_to_speech_request(
model=model,
input=input,
voice=voice,
optional_params=text_to_speech_optional_params,
litellm_params=litellm_params,
headers=headers,
)
# Merge provider-specific headers
if "headers" in request_data:
headers.update(request_data["headers"])
## LOGGING
logging_obj.pre_call(
input=input,
api_key="",
additional_args={
"complete_input_dict": request_data,
"api_base": api_base,
"headers": headers,
},
)
try:
# Determine request body type and send appropriately
if "dict_body" in request_data:
response = sync_httpx_client.post(
url=api_base,
headers=headers,
json=request_data["dict_body"],
timeout=timeout,
)
elif "ssml_body" in request_data:
response = sync_httpx_client.post(
url=api_base,
headers=headers,
data=request_data["ssml_body"],
timeout=timeout,
)
else:
raise ValueError(
"No body found in request_data. Must provide one of: dict_body, ssml_body, text_body, binary_body"
)
except Exception as e:
raise self._handle_error(
e=e,
provider_config=text_to_speech_provider_config,
)
return text_to_speech_provider_config.transform_text_to_speech_response(
model=model,
raw_response=response,
logging_obj=logging_obj,
)
async def async_text_to_speech_handler(
self,
model: str,
input: str,
voice: Optional[str],
text_to_speech_provider_config: BaseTextToSpeechConfig,
text_to_speech_optional_params: Dict,
custom_llm_provider: str,
litellm_params: Dict,
logging_obj: LiteLLMLoggingObj,
timeout: Union[float, httpx.Timeout],
extra_headers: Optional[Dict[str, Any]] = None,
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
) -> "HttpxBinaryResponseContent":
"""
Async version of the text-to-speech handler.
Uses async HTTP client to make requests.
"""
if client is None or not isinstance(client, AsyncHTTPHandler):
async_httpx_client = get_async_httpx_client(
llm_provider=litellm.LlmProviders(custom_llm_provider),
params={"ssl_verify": litellm_params.get("ssl_verify", None)},
)
else:
async_httpx_client = client
headers = text_to_speech_provider_config.validate_environment(
api_key=litellm_params.get("api_key"),
headers=extra_headers or {},
model=model,
api_base=litellm_params.get("api_base"),
)
if extra_headers:
headers.update(extra_headers)
api_base = text_to_speech_provider_config.get_complete_url(
model=model,
api_base=litellm_params.get("api_base"),
litellm_params=litellm_params,
)
request_data = text_to_speech_provider_config.transform_text_to_speech_request(
model=model,
input=input,
voice=voice,
optional_params=text_to_speech_optional_params,
litellm_params=litellm_params,
headers=headers,
)
# Merge provider-specific headers
if "headers" in request_data:
headers.update(request_data["headers"])
## LOGGING
logging_obj.pre_call(
input=input,
api_key="",
additional_args={
"complete_input_dict": request_data,
"api_base": api_base,
"headers": headers,
},
)
try:
# Determine request body type and send appropriately
if "dict_body" in request_data:
response = await async_httpx_client.post(
url=api_base,
headers=headers,
json=request_data["dict_body"],
timeout=timeout,
)
elif "ssml_body" in request_data:
response = await async_httpx_client.post(
url=api_base,
headers=headers,
data=request_data["ssml_body"],
timeout=timeout,
)
else:
raise ValueError(
"No body found in request_data. Must provide one of: dict_body, ssml_body, text_body, binary_body"
)
except Exception as e:
raise self._handle_error(
e=e,
provider_config=text_to_speech_provider_config,
)
return text_to_speech_provider_config.transform_text_to_speech_response(
model=model,
raw_response=response,
logging_obj=logging_obj,
)

View file

@ -158,6 +158,7 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig):
"parallel_tool_calls",
"audio",
"web_search_options",
"service_tier",
"safety_identifier",
] # works across all models

View file

@ -6,10 +6,12 @@ This requires websockets, and is currently only supported on LiteLLM Proxy.
from typing import Any, Optional, cast
from litellm.constants import REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES
from litellm.types.realtime import RealtimeQueryParams
from ....litellm_core_utils.litellm_logging import Logging as LiteLLMLogging
from ....litellm_core_utils.realtime_streaming import RealTimeStreaming
from ..openai import OpenAIChatCompletion
from litellm.types.realtime import RealtimeQueryParams
class OpenAIRealtime(OpenAIChatCompletion):
@ -59,6 +61,7 @@ class OpenAIRealtime(OpenAIChatCompletion):
"Authorization": f"Bearer {api_key}", # type: ignore
"OpenAI-Beta": "realtime=v1",
},
max_size=REALTIME_WEBSOCKET_MAX_MESSAGE_SIZE_BYTES,
) as backend_ws:
realtime_streaming = RealTimeStreaming(
websocket, cast(ClientConnection, backend_ws), logging_obj

View file

@ -380,6 +380,7 @@ async def acompletion(
Literal["none", "minimal", "low", "medium", "high", "default"]
] = None,
safety_identifier: Optional[str] = None,
service_tier: Optional[str] = None,
# set api_base, api_version, api_key
base_url: Optional[str] = None,
api_version: Optional[str] = None,
@ -529,6 +530,7 @@ async def acompletion(
"model_list": model_list,
"reasoning_effort": reasoning_effort,
"safety_identifier": safety_identifier,
"service_tier": service_tier,
"extra_headers": extra_headers,
"acompletion": True, # assuming this is a required parameter
"thinking": thinking,
@ -951,6 +953,7 @@ def completion( # type: ignore # noqa: PLR0915
deployment_id=None,
extra_headers: Optional[dict] = None,
safety_identifier: Optional[str] = None,
service_tier: Optional[str] = None,
# soon to be deprecated params by OpenAI
functions: Optional[List] = None,
function_call: Optional[str] = None,
@ -1293,6 +1296,7 @@ def completion( # type: ignore # noqa: PLR0915
"thinking": thinking,
"web_search_options": web_search_options,
"safety_identifier": safety_identifier,
"service_tier": service_tier,
"allowed_openai_params": kwargs.get("allowed_openai_params"),
}
optional_params = get_optional_params(
@ -5686,12 +5690,28 @@ def speech( # noqa: PLR0915
optional_params["speed"] = speed # type: ignore
if instructions is not None:
optional_params["instructions"] = instructions
if timeout is None:
timeout = litellm.request_timeout
if max_retries is None:
max_retries = litellm.num_retries or openai.DEFAULT_MAX_RETRIES
litellm_params_dict = get_litellm_params(**kwargs)
# Get provider-specific text-to-speech config and map parameters
text_to_speech_provider_config = ProviderConfigManager.get_provider_text_to_speech_config(
model=model,
provider=litellm.LlmProviders(custom_llm_provider),
)
# Map OpenAI params to provider-specific params if config exists
if text_to_speech_provider_config is not None:
optional_params = text_to_speech_provider_config.map_openai_params(
model=model,
optional_params=optional_params,
drop_params=False,
)
logging_obj: Logging = cast(Logging, kwargs.get("litellm_logging_obj"))
logging_obj.update_environment_variables(
model=model,
@ -5765,52 +5785,85 @@ def speech( # noqa: PLR0915
aspeech=aspeech,
)
elif custom_llm_provider == "azure":
# azure configs
if voice is None or not (isinstance(voice, str)):
raise litellm.BadRequestError(
message="'voice' is required to be passed as a string for Azure TTS",
model=model,
llm_provider=custom_llm_provider,
# Check if this is Azure Speech Service (Cognitive Services TTS)
if model.startswith("speech/"):
from litellm.llms.azure.text_to_speech.transformation import (
AzureAVATextToSpeechConfig,
)
api_base = api_base or litellm.api_base or get_secret("AZURE_API_BASE") # type: ignore
api_version = api_version or litellm.api_version or get_secret("AZURE_API_VERSION") # type: ignore
# Azure AVA (Cognitive Services) Text-to-Speech
if text_to_speech_provider_config is None:
raise litellm.BadRequestError(
message="Azure Speech Service configuration not found",
model=model,
llm_provider=custom_llm_provider,
)
api_key = (
api_key
or litellm.api_key
or litellm.azure_key
or get_secret("AZURE_OPENAI_API_KEY")
or get_secret("AZURE_API_KEY")
) # type: ignore
# Cast to specific Azure config type to access dispatch method
azure_config = cast(AzureAVATextToSpeechConfig, text_to_speech_provider_config)
response = azure_config.dispatch_text_to_speech( # type: ignore
model=model,
input=input,
voice=voice,
optional_params=optional_params,
litellm_params_dict=litellm_params_dict,
logging_obj=logging_obj,
timeout=timeout,
extra_headers=extra_headers,
base_llm_http_handler=base_llm_http_handler,
aspeech=aspeech or False,
api_base=api_base,
api_key=api_key,
**kwargs,
)
else:
# Azure OpenAI TTS
if voice is None or not (isinstance(voice, str)):
raise litellm.BadRequestError(
message="'voice' is required to be passed as a string for Azure TTS",
model=model,
llm_provider=custom_llm_provider,
)
api_base = api_base or litellm.api_base or get_secret("AZURE_API_BASE") # type: ignore
azure_ad_token: Optional[str] = optional_params.get("extra_body", {}).pop( # type: ignore
"azure_ad_token", None
) or get_secret(
"AZURE_AD_TOKEN"
)
azure_ad_token_provider = kwargs.get("azure_ad_token_provider", None)
api_version = api_version or litellm.api_version or get_secret("AZURE_API_VERSION") # type: ignore
if extra_headers:
optional_params["extra_headers"] = extra_headers
api_key = (
api_key
or litellm.api_key
or litellm.azure_key
or get_secret("AZURE_OPENAI_API_KEY")
or get_secret("AZURE_API_KEY")
) # type: ignore
response = azure_chat_completions.audio_speech(
model=model,
input=input,
voice=voice,
optional_params=optional_params,
api_key=api_key,
api_base=api_base,
api_version=api_version,
azure_ad_token=azure_ad_token,
azure_ad_token_provider=azure_ad_token_provider,
organization=organization,
max_retries=max_retries,
timeout=timeout,
client=client, # pass AsyncOpenAI, OpenAI client
aspeech=aspeech,
litellm_params=litellm_params_dict,
)
azure_ad_token: Optional[str] = optional_params.get("extra_body", {}).pop( # type: ignore
"azure_ad_token", None
) or get_secret(
"AZURE_AD_TOKEN"
)
azure_ad_token_provider = kwargs.get("azure_ad_token_provider", None)
if extra_headers:
optional_params["extra_headers"] = extra_headers
response = azure_chat_completions.audio_speech(
model=model,
input=input,
voice=voice,
optional_params=optional_params,
api_key=api_key,
api_base=api_base,
api_version=api_version,
azure_ad_token=azure_ad_token,
azure_ad_token_provider=azure_ad_token_provider,
organization=organization,
max_retries=max_retries,
timeout=timeout,
client=client, # pass AsyncOpenAI, OpenAI client
aspeech=aspeech,
litellm_params=litellm_params_dict,
)
elif custom_llm_provider == "vertex_ai" or custom_llm_provider == "vertex_ai_beta":
generic_optional_params = GenericLiteLLMParams(**kwargs)

View file

@ -934,7 +934,7 @@
"litellm_provider": "bedrock_converse",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.65e-05,
"search_context_cost_per_query": {
@ -2769,6 +2769,18 @@
"mode": "embedding",
"output_cost_per_token": 0.0
},
"azure/speech/azure-tts": {
"input_cost_per_character": 15e-06,
"litellm_provider": "azure",
"mode": "audio_speech",
"source": "https://azure.microsoft.com/en-us/pricing/calculator/"
},
"azure/speech/azure-tts-hd": {
"input_cost_per_character": 30e-06,
"litellm_provider": "azure",
"mode": "audio_speech",
"source": "https://azure.microsoft.com/en-us/pricing/calculator/"
},
"azure/tts-1": {
"input_cost_per_character": 1.5e-05,
"litellm_provider": "azure",
@ -4981,7 +4993,7 @@
"litellm_provider": "anthropic",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.5e-05,
"search_context_cost_per_query": {
@ -5011,7 +5023,7 @@
"litellm_provider": "anthropic",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.5e-05,
"search_context_cost_per_query": {
@ -8051,7 +8063,7 @@
"litellm_provider": "bedrock_converse",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.65e-05,
"search_context_cost_per_query": {
@ -12130,7 +12142,7 @@
"litellm_provider": "bedrock_converse",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.5e-05,
"search_context_cost_per_query": {
@ -12520,6 +12532,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.1-2025-04-14": {
@ -12553,6 +12566,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.1-mini": {
@ -12589,6 +12603,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.1-mini-2025-04-14": {
@ -12622,6 +12637,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.1-nano": {
@ -12658,6 +12674,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.1-nano-2025-04-14": {
@ -12691,6 +12708,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4.5-preview": {
@ -12755,6 +12773,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4o-2024-05-13": {
@ -12795,6 +12814,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4o-2024-11-20": {
@ -12815,6 +12835,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4o-audio-preview": {
@ -12906,6 +12927,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4o-mini-2024-07-18": {
@ -12931,6 +12953,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-4o-mini-audio-preview": {
@ -13248,6 +13271,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-5-pro": {
@ -13485,6 +13509,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-5-mini-2025-08-07": {
@ -13523,6 +13548,7 @@
"supports_response_schema": true,
"supports_system_messages": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"gpt-5-nano": {
@ -14737,7 +14763,7 @@
"litellm_provider": "bedrock_converse",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.65e-05,
"search_context_cost_per_query": {
@ -16543,6 +16569,7 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"o3-2025-04-16": {
@ -16574,6 +16601,7 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"o3-deep-research": {
@ -16758,6 +16786,7 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"o4-mini-2025-04-16": {
@ -16776,6 +16805,7 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_service_tier": true,
"supports_vision": true
},
"o4-mini-deep-research": {
@ -20612,7 +20642,7 @@
"litellm_provider": "bedrock_converse",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.65e-05,
"search_context_cost_per_query": {
@ -22031,7 +22061,7 @@
"litellm_provider": "vertex_ai-anthropic_models",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.5e-05,
"output_cost_per_token_batches": 7.5e-06,
@ -22057,7 +22087,7 @@
"litellm_provider": "vertex_ai-anthropic_models",
"max_input_tokens": 200000,
"max_output_tokens": 64000,
"max_tokens": 200000,
"max_tokens": 64000,
"mode": "chat",
"output_cost_per_token": 1.5e-05,
"output_cost_per_token_batches": 7.5e-06,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1979],{90286:function(e,n,t){Promise.resolve().then(t.bind(t,37492))},37492:function(e,n,t){"use strict";t.r(n);var r=t(57437),l=t(44696),s=t(80443);n.default=()=>{let{token:e,accessToken:n,userRole:t,userId:a,premiumUser:i}=(0,s.Z)();return(0,r.jsx)(l.Z,{accessToken:n,token:e,userRole:t,userID:a,premiumUser:i})}},80443:function(e,n,t){"use strict";var r=t(2265),l=t(99376),s=t(14474),a=t(3914);n.Z=()=>{var e,n,t,i,o,u,c;let d=(0,l.useRouter)(),m="undefined"!=typeof document?(0,a.e)("token"):null;(0,r.useEffect)(()=>{m||d.replace("/sso/key/generate")},[m,d]);let f=(0,r.useMemo)(()=>{if(!m)return null;try{return(0,s.o)(m)}catch(e){return(0,a.b)(),d.replace("/sso/key/generate"),null}},[m,d]);return{token:m,accessToken:null!==(e=null==f?void 0:f.key)&&void 0!==e?e:null,userId:null!==(n=null==f?void 0:f.user_id)&&void 0!==n?n:null,userEmail:null!==(t=null==f?void 0:f.user_email)&&void 0!==t?t:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(i=null==f?void 0:f.user_role)&&void 0!==i?i:null),premiumUser:null!==(o=null==f?void 0:f.premium_user)&&void 0!==o?o:null,disabledPersonalKeyCreation:null!==(u=null==f?void 0:f.disabled_non_admin_personal_key_creation)&&void 0!==u?u:null,showSSOBanner:(null==f?void 0:f.login_method)==="username_password"}}},39789:function(e,n,t){"use strict";t.d(n,{Z:function(){return i}});var r=t(57437),l=t(2265),s=t(21487),a=t(84264),i=e=>{let{value:n,onValueChange:t,label:i="Select Time Range",className:o="",showTimeRange:u=!0}=e,[c,d]=(0,l.useState)(!1),m=(0,l.useRef)(null),f=(0,l.useCallback)(e=>{d(!0),setTimeout(()=>d(!1),1500),t(e),requestIdleCallback(()=>{if(e.from){let n;let r={...e},l=new Date(e.from);n=new Date(e.to?e.to:e.from),l.toDateString(),n.toDateString(),l.setHours(0,0,0,0),n.setHours(23,59,59,999),r.from=l,r.to=n,t(r)}},{timeout:100})},[t]),h=(0,l.useCallback)((e,n)=>{if(!e||!n)return"";let t=e=>e.toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});if(e.toDateString()!==n.toDateString())return"".concat(t(e)," - ").concat(t(n));{let t=e.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}),r=e.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0}),l=n.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});return"".concat(t,": ").concat(r," - ").concat(l)}},[]);return(0,r.jsxs)("div",{className:o,children:[i&&(0,r.jsx)(a.Z,{className:"mb-2",children:i}),(0,r.jsxs)("div",{className:"relative w-fit",children:[(0,r.jsx)("div",{ref:m,children:(0,r.jsx)(s.Z,{enableSelect:!0,value:n,onValueChange:f,placeholder:"Select date range",enableClear:!1,style:{zIndex:100}})}),c&&(0,r.jsx)("div",{className:"absolute top-1/2 animate-pulse",style:{left:"calc(100% + 8px)",transform:"translateY(-50%)",zIndex:110},children:(0,r.jsxs)("div",{className:"flex items-center gap-1 text-green-600 text-sm font-medium bg-white px-2 py-1 rounded-full border border-green-200 shadow-sm whitespace-nowrap",children:[(0,r.jsx)("div",{className:"w-3 h-3 bg-green-500 text-white rounded-full flex items-center justify-center text-xs",children:"✓"}),(0,r.jsx)("span",{className:"text-xs",children:"Selected"})]})})]}),u&&n.from&&n.to&&(0,r.jsx)(a.Z,{className:"mt-2 text-xs text-gray-500",children:h(n.from,n.to)})]})}}},function(e){e.O(0,[9820,1491,1526,2926,9678,7281,2344,1487,2662,8049,4696,2971,2117,1744],function(){return e(e.s=90286)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1979],{25886:function(e,n,t){Promise.resolve().then(t.bind(t,37492))},37492:function(e,n,t){"use strict";t.r(n);var r=t(57437),l=t(44696),s=t(39760);n.default=()=>{let{token:e,accessToken:n,userRole:t,userId:a,premiumUser:i}=(0,s.Z)();return(0,r.jsx)(l.Z,{accessToken:n,token:e,userRole:t,userID:a,premiumUser:i})}},39760:function(e,n,t){"use strict";var r=t(2265),l=t(99376),s=t(14474),a=t(3914);n.Z=()=>{var e,n,t,i,o,u,c;let d=(0,l.useRouter)(),m="undefined"!=typeof document?(0,a.e)("token"):null;(0,r.useEffect)(()=>{m||d.replace("/sso/key/generate")},[m,d]);let f=(0,r.useMemo)(()=>{if(!m)return null;try{return(0,s.o)(m)}catch(e){return(0,a.b)(),d.replace("/sso/key/generate"),null}},[m,d]);return{token:m,accessToken:null!==(e=null==f?void 0:f.key)&&void 0!==e?e:null,userId:null!==(n=null==f?void 0:f.user_id)&&void 0!==n?n:null,userEmail:null!==(t=null==f?void 0:f.user_email)&&void 0!==t?t:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(i=null==f?void 0:f.user_role)&&void 0!==i?i:null),premiumUser:null!==(o=null==f?void 0:f.premium_user)&&void 0!==o?o:null,disabledPersonalKeyCreation:null!==(u=null==f?void 0:f.disabled_non_admin_personal_key_creation)&&void 0!==u?u:null,showSSOBanner:(null==f?void 0:f.login_method)==="username_password"}}},39789:function(e,n,t){"use strict";t.d(n,{Z:function(){return i}});var r=t(57437),l=t(2265),s=t(21487),a=t(84264),i=e=>{let{value:n,onValueChange:t,label:i="Select Time Range",className:o="",showTimeRange:u=!0}=e,[c,d]=(0,l.useState)(!1),m=(0,l.useRef)(null),f=(0,l.useCallback)(e=>{d(!0),setTimeout(()=>d(!1),1500),t(e),requestIdleCallback(()=>{if(e.from){let n;let r={...e},l=new Date(e.from);n=new Date(e.to?e.to:e.from),l.toDateString(),n.toDateString(),l.setHours(0,0,0,0),n.setHours(23,59,59,999),r.from=l,r.to=n,t(r)}},{timeout:100})},[t]),h=(0,l.useCallback)((e,n)=>{if(!e||!n)return"";let t=e=>e.toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});if(e.toDateString()!==n.toDateString())return"".concat(t(e)," - ").concat(t(n));{let t=e.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}),r=e.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0}),l=n.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!0,timeZoneName:"short"});return"".concat(t,": ").concat(r," - ").concat(l)}},[]);return(0,r.jsxs)("div",{className:o,children:[i&&(0,r.jsx)(a.Z,{className:"mb-2",children:i}),(0,r.jsxs)("div",{className:"relative w-fit",children:[(0,r.jsx)("div",{ref:m,children:(0,r.jsx)(s.Z,{enableSelect:!0,value:n,onValueChange:f,placeholder:"Select date range",enableClear:!1,style:{zIndex:100}})}),c&&(0,r.jsx)("div",{className:"absolute top-1/2 animate-pulse",style:{left:"calc(100% + 8px)",transform:"translateY(-50%)",zIndex:110},children:(0,r.jsxs)("div",{className:"flex items-center gap-1 text-green-600 text-sm font-medium bg-white px-2 py-1 rounded-full border border-green-200 shadow-sm whitespace-nowrap",children:[(0,r.jsx)("div",{className:"w-3 h-3 bg-green-500 text-white rounded-full flex items-center justify-center text-xs",children:"✓"}),(0,r.jsx)("span",{className:"text-xs",children:"Selected"})]})})]}),u&&n.from&&n.to&&(0,r.jsx)(a.Z,{className:"mt-2 text-xs text-gray-500",children:h(n.from,n.to)})]})}}},function(e){e.O(0,[9820,1491,1526,2926,9678,7281,2344,1487,2662,8049,4696,2971,2117,1744],function(){return e(e.s=25886)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2099],{71620:function(e,n,r){Promise.resolve().then(r.bind(r,51599))},84717:function(e,n,r){"use strict";r.d(n,{Ct:function(){return t.Z},Dx:function(){return m.Z},OK:function(){return l.Z},Zb:function(){return i.Z},nP:function(){return d.Z},rj:function(){return o.Z},td:function(){return a.Z},v0:function(){return c.Z},x4:function(){return s.Z},xv:function(){return f.Z},zx:function(){return u.Z}});var t=r(41649),u=r(20831),i=r(12514),o=r(67101),l=r(12485),c=r(18135),a=r(35242),s=r(29706),d=r(77991),f=r(84264),m=r(96761)},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return t.Z}});var t=r(20831)},51599:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(30603),i=r(80443);n.default=()=>{let{accessToken:e}=(0,i.Z)();return(0,t.jsx)(u.Z,{accessToken:e})}},80443:function(e,n,r){"use strict";var t=r(2265),u=r(99376),i=r(14474),o=r(3914);n.Z=()=>{var e,n,r,l,c,a,s;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,o.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let m=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,i.o)(f)}catch(e){return(0,o.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==m?void 0:m.key)&&void 0!==e?e:null,userId:null!==(n=null==m?void 0:m.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==m?void 0:m.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(l=null==m?void 0:m.user_role)&&void 0!==l?l:null),premiumUser:null!==(c=null==m?void 0:m.premium_user)&&void 0!==c?c:null,disabledPersonalKeyCreation:null!==(a=null==m?void 0:m.disabled_non_admin_personal_key_creation)&&void 0!==a?a:null,showSSOBanner:(null==m?void 0:m.login_method)==="username_password"}}},59872:function(e,n,r){"use strict";r.d(n,{nl:function(){return u},pw:function(){return i},vQ:function(){return o}});var t=r(9114);function u(e,n){let r=structuredClone(e);for(let[e,t]of Object.entries(n))e in r&&(r[e]=t);return r}let i=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null==e||!Number.isFinite(e))return"-";let t={minimumFractionDigits:n,maximumFractionDigits:n};if(!r)return e.toLocaleString("en-US",t);let u=Math.abs(e),i=u,o="";return u>=1e6?(i=u/1e6,o="M"):u>=1e3&&(i=u/1e3,o="K"),"".concat(e<0?"-":"").concat(i.toLocaleString("en-US",t)).concat(o)},o=async function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Copied to clipboard";if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,n);try{return await navigator.clipboard.writeText(e),t.Z.success(n),!0}catch(r){return console.error("Clipboard API failed: ",r),l(e,n)}},l=(e,n)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let u=document.execCommand("copy");if(document.body.removeChild(r),u)return t.Z.success(n),!0;throw Error("execCommand failed")}catch(e){return t.Z.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}}},20347:function(e,n,r){"use strict";r.d(n,{LQ:function(){return i},ZL:function(){return t},lo:function(){return u},tY:function(){return o}});let t=["Admin","Admin Viewer","proxy_admin","proxy_admin_viewer","org_admin"],u=["Internal User","Internal Viewer"],i=["Internal User","Admin","proxy_admin"],o=e=>t.includes(e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,2525,9011,5319,8347,8049,603,2971,2117,1744],function(){return e(e.s=71620)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2099],{11673:function(e,n,r){Promise.resolve().then(r.bind(r,51599))},84717:function(e,n,r){"use strict";r.d(n,{Ct:function(){return t.Z},Dx:function(){return m.Z},OK:function(){return l.Z},Zb:function(){return i.Z},nP:function(){return d.Z},rj:function(){return o.Z},td:function(){return a.Z},v0:function(){return c.Z},x4:function(){return s.Z},xv:function(){return f.Z},zx:function(){return u.Z}});var t=r(41649),u=r(20831),i=r(12514),o=r(67101),l=r(12485),c=r(18135),a=r(35242),s=r(29706),d=r(77991),f=r(84264),m=r(96761)},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return t.Z}});var t=r(20831)},51599:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(30603),i=r(39760);n.default=()=>{let{accessToken:e}=(0,i.Z)();return(0,t.jsx)(u.Z,{accessToken:e})}},39760:function(e,n,r){"use strict";var t=r(2265),u=r(99376),i=r(14474),o=r(3914);n.Z=()=>{var e,n,r,l,c,a,s;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,o.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let m=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,i.o)(f)}catch(e){return(0,o.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==m?void 0:m.key)&&void 0!==e?e:null,userId:null!==(n=null==m?void 0:m.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==m?void 0:m.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(l=null==m?void 0:m.user_role)&&void 0!==l?l:null),premiumUser:null!==(c=null==m?void 0:m.premium_user)&&void 0!==c?c:null,disabledPersonalKeyCreation:null!==(a=null==m?void 0:m.disabled_non_admin_personal_key_creation)&&void 0!==a?a:null,showSSOBanner:(null==m?void 0:m.login_method)==="username_password"}}},59872:function(e,n,r){"use strict";r.d(n,{nl:function(){return u},pw:function(){return i},vQ:function(){return o}});var t=r(9114);function u(e,n){let r=structuredClone(e);for(let[e,t]of Object.entries(n))e in r&&(r[e]=t);return r}let i=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null==e||!Number.isFinite(e))return"-";let t={minimumFractionDigits:n,maximumFractionDigits:n};if(!r)return e.toLocaleString("en-US",t);let u=Math.abs(e),i=u,o="";return u>=1e6?(i=u/1e6,o="M"):u>=1e3&&(i=u/1e3,o="K"),"".concat(e<0?"-":"").concat(i.toLocaleString("en-US",t)).concat(o)},o=async function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Copied to clipboard";if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,n);try{return await navigator.clipboard.writeText(e),t.Z.success(n),!0}catch(r){return console.error("Clipboard API failed: ",r),l(e,n)}},l=(e,n)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let u=document.execCommand("copy");if(document.body.removeChild(r),u)return t.Z.success(n),!0;throw Error("execCommand failed")}catch(e){return t.Z.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}}},20347:function(e,n,r){"use strict";r.d(n,{LQ:function(){return i},ZL:function(){return t},lo:function(){return u},tY:function(){return o}});let t=["Admin","Admin Viewer","proxy_admin","proxy_admin_viewer","org_admin"],u=["Internal User","Internal Viewer"],i=["Internal User","Admin","proxy_admin"],o=e=>t.includes(e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,2525,9011,5319,8347,8049,603,2971,2117,1744],function(){return e(e.s=11673)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6061],{86947:function(n,u,e){Promise.resolve().then(e.bind(e,21933))},45822:function(n,u,e){"use strict";e.d(u,{JO:function(){return s.Z},JX:function(){return t.Z},rj:function(){return c.Z},xv:function(){return i.Z},zx:function(){return r.Z}});var r=e(20831),t=e(49804),c=e(67101),s=e(47323),i=e(84264)},21933:function(n,u,e){"use strict";e.r(u);var r=e(57437),t=e(42273),c=e(80443);u.default=()=>{let{accessToken:n,userId:u,userRole:e}=(0,c.Z)();return(0,r.jsx)(t.Z,{accessToken:n,userID:u,userRole:e})}}},function(n){n.O(0,[9820,1491,1526,2417,3709,9775,2525,1529,2284,7908,9011,9678,3603,5319,1853,6494,5188,4924,8049,131,2202,874,2273,2971,2117,1744],function(){return n(n.s=86947)}),_N_E=n.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6061],{19117:function(n,u,e){Promise.resolve().then(e.bind(e,21933))},45822:function(n,u,e){"use strict";e.d(u,{JO:function(){return s.Z},JX:function(){return t.Z},rj:function(){return c.Z},xv:function(){return i.Z},zx:function(){return r.Z}});var r=e(20831),t=e(49804),c=e(67101),s=e(47323),i=e(84264)},21933:function(n,u,e){"use strict";e.r(u);var r=e(57437),t=e(42273),c=e(39760);u.default=()=>{let{accessToken:n,userId:u,userRole:e}=(0,c.Z)();return(0,r.jsx)(t.Z,{accessToken:n,userID:u,userRole:e})}}},function(n){n.O(0,[9820,1491,1526,2417,3709,9775,2525,1529,2284,7908,9011,9678,3603,5319,1853,6494,5188,4924,8049,131,2202,874,2273,2971,2117,1744],function(){return n(n.s=19117)}),_N_E=n.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6607],{91229:function(e,n,r){Promise.resolve().then(r.bind(r,49514))},30078:function(e,n,r){"use strict";r.d(n,{Ct:function(){return t.Z},Dx:function(){return p.Z},OK:function(){return l.Z},Zb:function(){return i.Z},nP:function(){return d.Z},oi:function(){return m.Z},rj:function(){return o.Z},td:function(){return a.Z},v0:function(){return c.Z},x4:function(){return s.Z},xv:function(){return f.Z},zx:function(){return u.Z}});var t=r(41649),u=r(20831),i=r(12514),o=r(67101),l=r(12485),c=r(18135),a=r(35242),s=r(29706),d=r(77991),f=r(84264),m=r(49566),p=r(96761)},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return t.Z}});var t=r(20831)},64504:function(e,n,r){"use strict";r.d(n,{o:function(){return u.Z},z:function(){return t.Z}});var t=r(20831),u=r(49566)},49514:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(63298),i=r(80443);n.default=()=>{let{accessToken:e}=(0,i.Z)();return(0,t.jsx)(u.Z,{accessToken:e})}},80443:function(e,n,r){"use strict";var t=r(2265),u=r(99376),i=r(14474),o=r(3914);n.Z=()=>{var e,n,r,l,c,a,s;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,o.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let m=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,i.o)(f)}catch(e){return(0,o.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==m?void 0:m.key)&&void 0!==e?e:null,userId:null!==(n=null==m?void 0:m.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==m?void 0:m.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(l=null==m?void 0:m.user_role)&&void 0!==l?l:null),premiumUser:null!==(c=null==m?void 0:m.premium_user)&&void 0!==c?c:null,disabledPersonalKeyCreation:null!==(a=null==m?void 0:m.disabled_non_admin_personal_key_creation)&&void 0!==a?a:null,showSSOBanner:(null==m?void 0:m.login_method)==="username_password"}}},24199:function(e,n,r){"use strict";r.d(n,{Z:function(){return i}});var t=r(57437);r(2265);var u=r(30150),i=e=>{let{step:n=.01,style:r={width:"100%"},placeholder:i="Enter a numerical value",min:o,max:l,onChange:c,...a}=e;return(0,t.jsx)(u.Z,{onWheel:e=>e.currentTarget.blur(),step:n,style:r,placeholder:i,min:o,max:l,onChange:c,...a})}},59872:function(e,n,r){"use strict";r.d(n,{nl:function(){return u},pw:function(){return i},vQ:function(){return o}});var t=r(9114);function u(e,n){let r=structuredClone(e);for(let[e,t]of Object.entries(n))e in r&&(r[e]=t);return r}let i=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null==e||!Number.isFinite(e))return"-";let t={minimumFractionDigits:n,maximumFractionDigits:n};if(!r)return e.toLocaleString("en-US",t);let u=Math.abs(e),i=u,o="";return u>=1e6?(i=u/1e6,o="M"):u>=1e3&&(i=u/1e3,o="K"),"".concat(e<0?"-":"").concat(i.toLocaleString("en-US",t)).concat(o)},o=async function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Copied to clipboard";if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,n);try{return await navigator.clipboard.writeText(e),t.Z.success(n),!0}catch(r){return console.error("Clipboard API failed: ",r),l(e,n)}},l=(e,n)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let u=document.execCommand("copy");if(document.body.removeChild(r),u)return t.Z.success(n),!0;throw Error("execCommand failed")}catch(e){return t.Z.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}}},20347:function(e,n,r){"use strict";r.d(n,{LQ:function(){return i},ZL:function(){return t},lo:function(){return u},tY:function(){return o}});let t=["Admin","Admin Viewer","proxy_admin","proxy_admin_viewer","org_admin"],u=["Internal User","Internal Viewer"],i=["Internal User","Admin","proxy_admin"],o=e=>t.includes(e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,3709,9775,2525,2284,7908,9011,3752,3866,5830,8049,3298,2971,2117,1744],function(){return e(e.s=91229)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[6607],{88558:function(e,n,r){Promise.resolve().then(r.bind(r,49514))},30078:function(e,n,r){"use strict";r.d(n,{Ct:function(){return t.Z},Dx:function(){return p.Z},OK:function(){return l.Z},Zb:function(){return i.Z},nP:function(){return d.Z},oi:function(){return m.Z},rj:function(){return o.Z},td:function(){return a.Z},v0:function(){return c.Z},x4:function(){return s.Z},xv:function(){return f.Z},zx:function(){return u.Z}});var t=r(41649),u=r(20831),i=r(12514),o=r(67101),l=r(12485),c=r(18135),a=r(35242),s=r(29706),d=r(77991),f=r(84264),m=r(49566),p=r(96761)},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return t.Z}});var t=r(20831)},64504:function(e,n,r){"use strict";r.d(n,{o:function(){return u.Z},z:function(){return t.Z}});var t=r(20831),u=r(49566)},49514:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(63298),i=r(39760);n.default=()=>{let{accessToken:e}=(0,i.Z)();return(0,t.jsx)(u.Z,{accessToken:e})}},39760:function(e,n,r){"use strict";var t=r(2265),u=r(99376),i=r(14474),o=r(3914);n.Z=()=>{var e,n,r,l,c,a,s;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,o.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let m=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,i.o)(f)}catch(e){return(0,o.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==m?void 0:m.key)&&void 0!==e?e:null,userId:null!==(n=null==m?void 0:m.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==m?void 0:m.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(l=null==m?void 0:m.user_role)&&void 0!==l?l:null),premiumUser:null!==(c=null==m?void 0:m.premium_user)&&void 0!==c?c:null,disabledPersonalKeyCreation:null!==(a=null==m?void 0:m.disabled_non_admin_personal_key_creation)&&void 0!==a?a:null,showSSOBanner:(null==m?void 0:m.login_method)==="username_password"}}},24199:function(e,n,r){"use strict";r.d(n,{Z:function(){return i}});var t=r(57437);r(2265);var u=r(30150),i=e=>{let{step:n=.01,style:r={width:"100%"},placeholder:i="Enter a numerical value",min:o,max:l,onChange:c,...a}=e;return(0,t.jsx)(u.Z,{onWheel:e=>e.currentTarget.blur(),step:n,style:r,placeholder:i,min:o,max:l,onChange:c,...a})}},59872:function(e,n,r){"use strict";r.d(n,{nl:function(){return u},pw:function(){return i},vQ:function(){return o}});var t=r(9114);function u(e,n){let r=structuredClone(e);for(let[e,t]of Object.entries(n))e in r&&(r[e]=t);return r}let i=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null==e||!Number.isFinite(e))return"-";let t={minimumFractionDigits:n,maximumFractionDigits:n};if(!r)return e.toLocaleString("en-US",t);let u=Math.abs(e),i=u,o="";return u>=1e6?(i=u/1e6,o="M"):u>=1e3&&(i=u/1e3,o="K"),"".concat(e<0?"-":"").concat(i.toLocaleString("en-US",t)).concat(o)},o=async function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"Copied to clipboard";if(!e)return!1;if(!navigator||!navigator.clipboard||!navigator.clipboard.writeText)return l(e,n);try{return await navigator.clipboard.writeText(e),t.Z.success(n),!0}catch(r){return console.error("Clipboard API failed: ",r),l(e,n)}},l=(e,n)=>{try{let r=document.createElement("textarea");r.value=e,r.style.position="fixed",r.style.left="-999999px",r.style.top="-999999px",r.setAttribute("readonly",""),document.body.appendChild(r),r.focus(),r.select();let u=document.execCommand("copy");if(document.body.removeChild(r),u)return t.Z.success(n),!0;throw Error("execCommand failed")}catch(e){return t.Z.fromBackend("Failed to copy to clipboard"),console.error("Failed to copy: ",e),!1}}},20347:function(e,n,r){"use strict";r.d(n,{LQ:function(){return i},ZL:function(){return t},lo:function(){return u},tY:function(){return o}});let t=["Admin","Admin Viewer","proxy_admin","proxy_admin_viewer","org_admin"],u=["Internal User","Internal Viewer"],i=["Internal User","Admin","proxy_admin"],o=e=>t.includes(e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,3709,9775,2525,2284,7908,9011,3752,3866,5830,8049,3298,2971,2117,1744],function(){return e(e.s=88558)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8958],{22489:function(e,n,r){Promise.resolve().then(r.bind(r,8786))},25512:function(e,n,r){"use strict";r.d(n,{P:function(){return t.Z},Q:function(){return u.Z}});var t=r(27281),u=r(57365)},56522:function(e,n,r){"use strict";r.d(n,{o:function(){return u.Z},x:function(){return t.Z}});var t=r(84264),u=r(49566)},80443:function(e,n,r){"use strict";var t=r(2265),u=r(99376),l=r(14474),i=r(3914);n.Z=()=>{var e,n,r,a,o,s,c;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,i.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let _=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,l.o)(f)}catch(e){return(0,i.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==_?void 0:_.key)&&void 0!==e?e:null,userId:null!==(n=null==_?void 0:_.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==_?void 0:_.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(a=null==_?void 0:_.user_role)&&void 0!==a?a:null),premiumUser:null!==(o=null==_?void 0:_.premium_user)&&void 0!==o?o:null,disabledPersonalKeyCreation:null!==(s=null==_?void 0:_.disabled_non_admin_personal_key_creation)&&void 0!==s?s:null,showSSOBanner:(null==_?void 0:_.login_method)==="username_password"}}},11318:function(e,n,r){"use strict";r.d(n,{Z:function(){return a}});var t=r(2265),u=r(80443),l=r(19250);let i=async(e,n,r,t)=>"Admin"!=r&&"Admin Viewer"!=r?await (0,l.teamListCall)(e,(null==t?void 0:t.organization_id)||null,n):await (0,l.teamListCall)(e,(null==t?void 0:t.organization_id)||null);var a=()=>{let[e,n]=(0,t.useState)([]),{accessToken:r,userId:l,userRole:a}=(0,u.Z)();return(0,t.useEffect)(()=>{(async()=>{n(await i(r,l,a,null))})()},[r,l,a]),{teams:e,setTeams:n}}},8786:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(90773),l=r(80443),i=r(2265),a=r(11318);n.default=()=>{let{teams:e,setTeams:n}=(0,a.Z)(),[r,o]=(0,i.useState)(()=>new URLSearchParams(window.location.search)),{accessToken:s,userId:c,premiumUser:d,showSSOBanner:f}=(0,l.Z)();return(0,t.jsx)(u.Z,{searchParams:r,accessToken:s,userID:c,setTeams:n,showSSOBanner:f,premiumUser:d})}},12363:function(e,n,r){"use strict";r.d(n,{d:function(){return l},n:function(){return u}});var t=r(2265);let u=()=>{let[e,n]=(0,t.useState)("http://localhost:4000");return(0,t.useEffect)(()=>{{let{protocol:e,host:r}=window.location;n("".concat(e,"//").concat(r))}},[]),e},l=25}},function(e){e.O(0,[9820,1491,1526,2417,2926,9775,9678,7281,2052,8049,773,2971,2117,1744],function(){return e(e.s=22489)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8958],{46710:function(e,n,r){Promise.resolve().then(r.bind(r,8786))},25512:function(e,n,r){"use strict";r.d(n,{P:function(){return t.Z},Q:function(){return u.Z}});var t=r(27281),u=r(57365)},56522:function(e,n,r){"use strict";r.d(n,{o:function(){return u.Z},x:function(){return t.Z}});var t=r(84264),u=r(49566)},39760:function(e,n,r){"use strict";var t=r(2265),u=r(99376),l=r(14474),i=r(3914);n.Z=()=>{var e,n,r,a,o,s,c;let d=(0,u.useRouter)(),f="undefined"!=typeof document?(0,i.e)("token"):null;(0,t.useEffect)(()=>{f||d.replace("/sso/key/generate")},[f,d]);let _=(0,t.useMemo)(()=>{if(!f)return null;try{return(0,l.o)(f)}catch(e){return(0,i.b)(),d.replace("/sso/key/generate"),null}},[f,d]);return{token:f,accessToken:null!==(e=null==_?void 0:_.key)&&void 0!==e?e:null,userId:null!==(n=null==_?void 0:_.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==_?void 0:_.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(a=null==_?void 0:_.user_role)&&void 0!==a?a:null),premiumUser:null!==(o=null==_?void 0:_.premium_user)&&void 0!==o?o:null,disabledPersonalKeyCreation:null!==(s=null==_?void 0:_.disabled_non_admin_personal_key_creation)&&void 0!==s?s:null,showSSOBanner:(null==_?void 0:_.login_method)==="username_password"}}},11318:function(e,n,r){"use strict";r.d(n,{Z:function(){return a}});var t=r(2265),u=r(39760),l=r(19250);let i=async(e,n,r,t)=>"Admin"!=r&&"Admin Viewer"!=r?await (0,l.teamListCall)(e,(null==t?void 0:t.organization_id)||null,n):await (0,l.teamListCall)(e,(null==t?void 0:t.organization_id)||null);var a=()=>{let[e,n]=(0,t.useState)([]),{accessToken:r,userId:l,userRole:a}=(0,u.Z)();return(0,t.useEffect)(()=>{(async()=>{n(await i(r,l,a,null))})()},[r,l,a]),{teams:e,setTeams:n}}},8786:function(e,n,r){"use strict";r.r(n);var t=r(57437),u=r(90773),l=r(39760),i=r(2265),a=r(11318);n.default=()=>{let{teams:e,setTeams:n}=(0,a.Z)(),[r,o]=(0,i.useState)(()=>new URLSearchParams(window.location.search)),{accessToken:s,userId:c,premiumUser:d,showSSOBanner:f}=(0,l.Z)();return(0,t.jsx)(u.Z,{searchParams:r,accessToken:s,userID:c,setTeams:n,showSSOBanner:f,premiumUser:d})}},12363:function(e,n,r){"use strict";r.d(n,{d:function(){return l},n:function(){return u}});var t=r(2265);let u=()=>{let[e,n]=(0,t.useState)("http://localhost:4000");return(0,t.useEffect)(()=>{{let{protocol:e,host:r}=window.location;n("".concat(e,"//").concat(r))}},[]),e},l=25}},function(e){e.O(0,[9820,1491,1526,2417,2926,9775,9678,7281,2052,8049,773,2971,2117,1744],function(){return e(e.s=46710)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2445],{96354:function(e,n,a){Promise.resolve().then(a.bind(a,72719))},80443:function(e,n,a){"use strict";var t=a(2265),r=a(99376),s=a(14474),i=a(3914);n.Z=()=>{var e,n,a,o,l,g,u;let _=(0,r.useRouter)(),p="undefined"!=typeof document?(0,i.e)("token"):null;(0,t.useEffect)(()=>{p||_.replace("/sso/key/generate")},[p,_]);let d=(0,t.useMemo)(()=>{if(!p)return null;try{return(0,s.o)(p)}catch(e){return(0,i.b)(),_.replace("/sso/key/generate"),null}},[p,_]);return{token:p,accessToken:null!==(e=null==d?void 0:d.key)&&void 0!==e?e:null,userId:null!==(n=null==d?void 0:d.user_id)&&void 0!==n?n:null,userEmail:null!==(a=null==d?void 0:d.user_email)&&void 0!==a?a:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(o=null==d?void 0:d.user_role)&&void 0!==o?o:null),premiumUser:null!==(l=null==d?void 0:d.premium_user)&&void 0!==l?l:null,disabledPersonalKeyCreation:null!==(g=null==d?void 0:d.disabled_non_admin_personal_key_creation)&&void 0!==g?g:null,showSSOBanner:(null==d?void 0:d.login_method)==="username_password"}}},72719:function(e,n,a){"use strict";a.r(n);var t=a(57437),r=a(6925),s=a(80443);n.default=()=>{let{accessToken:e,userRole:n,userId:a,premiumUser:i}=(0,s.Z)();return(0,t.jsx)(r.Z,{accessToken:e,userRole:n,userID:a,premiumUser:i})}},97434:function(e,n,a){"use strict";a.d(n,{Dg:function(){return s},Lo:function(){return i},O0:function(){return r},PA:function(){return g},RD:function(){return o},Z3:function(){return l},_3:function(){return u}});let t="/ui/assets/logos/",r=[{id:"arize",displayName:"Arize",logo:"".concat(t,"arize.png"),supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_key:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:"".concat(t,"braintrust.png"),supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:"".concat(t,"custom.svg"),supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"datadog",displayName:"Datadog",logo:"".concat(t,"datadog.png"),supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:"".concat(t,"lago.svg"),supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:"".concat(t,"langfuse.png"),supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:"".concat(t,"langfuse.png"),supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:"".concat(t,"langsmith.png"),supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:"".concat(t,"openmeter.png"),supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:"".concat(t,"otel.png"),supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:"".concat(t,"aws.svg"),supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"}],s=r.reduce((e,n)=>(e[n.displayName]=n,e),{}),i=r.reduce((e,n)=>(e[n.displayName]=n.id,e),{}),o=r.reduce((e,n)=>(e[n.id]=n.displayName,e),{}),l=e=>e.map(e=>i[e]||e),g=e=>e.map(e=>o[e]||e),u=e=>r.find(n=>n.id===e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,9775,2284,7908,9678,226,8049,6925,2971,2117,1744],function(){return e(e.s=96354)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2445],{75327:function(e,n,a){Promise.resolve().then(a.bind(a,72719))},39760:function(e,n,a){"use strict";var t=a(2265),r=a(99376),s=a(14474),i=a(3914);n.Z=()=>{var e,n,a,o,l,g,u;let _=(0,r.useRouter)(),p="undefined"!=typeof document?(0,i.e)("token"):null;(0,t.useEffect)(()=>{p||_.replace("/sso/key/generate")},[p,_]);let d=(0,t.useMemo)(()=>{if(!p)return null;try{return(0,s.o)(p)}catch(e){return(0,i.b)(),_.replace("/sso/key/generate"),null}},[p,_]);return{token:p,accessToken:null!==(e=null==d?void 0:d.key)&&void 0!==e?e:null,userId:null!==(n=null==d?void 0:d.user_id)&&void 0!==n?n:null,userEmail:null!==(a=null==d?void 0:d.user_email)&&void 0!==a?a:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(o=null==d?void 0:d.user_role)&&void 0!==o?o:null),premiumUser:null!==(l=null==d?void 0:d.premium_user)&&void 0!==l?l:null,disabledPersonalKeyCreation:null!==(g=null==d?void 0:d.disabled_non_admin_personal_key_creation)&&void 0!==g?g:null,showSSOBanner:(null==d?void 0:d.login_method)==="username_password"}}},72719:function(e,n,a){"use strict";a.r(n);var t=a(57437),r=a(6925),s=a(39760);n.default=()=>{let{accessToken:e,userRole:n,userId:a,premiumUser:i}=(0,s.Z)();return(0,t.jsx)(r.Z,{accessToken:e,userRole:n,userID:a,premiumUser:i})}},97434:function(e,n,a){"use strict";a.d(n,{Dg:function(){return s},Lo:function(){return i},O0:function(){return r},PA:function(){return g},RD:function(){return o},Z3:function(){return l},_3:function(){return u}});let t="/ui/assets/logos/",r=[{id:"arize",displayName:"Arize",logo:"".concat(t,"arize.png"),supports_key_team_logging:!0,dynamic_params:{arize_api_key:"password",arize_space_key:"password"},description:"Arize Logging Integration"},{id:"braintrust",displayName:"Braintrust",logo:"".concat(t,"braintrust.png"),supports_key_team_logging:!1,dynamic_params:{braintrust_api_key:"password",braintrust_project_name:"text"},description:"Braintrust Logging Integration"},{id:"custom_callback_api",displayName:"Custom Callback API",logo:"".concat(t,"custom.svg"),supports_key_team_logging:!0,dynamic_params:{custom_callback_api_url:"text",custom_callback_api_headers:"text"},description:"Custom Callback API Logging Integration"},{id:"datadog",displayName:"Datadog",logo:"".concat(t,"datadog.png"),supports_key_team_logging:!1,dynamic_params:{dd_api_key:"password",dd_site:"text"},description:"Datadog Logging Integration"},{id:"lago",displayName:"Lago",logo:"".concat(t,"lago.svg"),supports_key_team_logging:!1,dynamic_params:{lago_api_url:"text",lago_api_key:"password"},description:"Lago Billing Logging Integration"},{id:"langfuse",displayName:"Langfuse",logo:"".concat(t,"langfuse.png"),supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v2 Logging Integration"},{id:"langfuse_otel",displayName:"Langfuse OTEL",logo:"".concat(t,"langfuse.png"),supports_key_team_logging:!0,dynamic_params:{langfuse_public_key:"text",langfuse_secret_key:"password",langfuse_host:"text"},description:"Langfuse v3 OTEL Logging Integration"},{id:"langsmith",displayName:"LangSmith",logo:"".concat(t,"langsmith.png"),supports_key_team_logging:!0,dynamic_params:{langsmith_api_key:"password",langsmith_project:"text",langsmith_base_url:"text",langsmith_sampling_rate:"number"},description:"Langsmith Logging Integration"},{id:"openmeter",displayName:"OpenMeter",logo:"".concat(t,"openmeter.png"),supports_key_team_logging:!1,dynamic_params:{openmeter_api_key:"password",openmeter_base_url:"text"},description:"OpenMeter Logging Integration"},{id:"otel",displayName:"Open Telemetry",logo:"".concat(t,"otel.png"),supports_key_team_logging:!1,dynamic_params:{otel_endpoint:"text",otel_headers:"text"},description:"OpenTelemetry Logging Integration"},{id:"s3",displayName:"S3",logo:"".concat(t,"aws.svg"),supports_key_team_logging:!1,dynamic_params:{s3_bucket_name:"text",aws_access_key_id:"password",aws_secret_access_key:"password",aws_region:"text"},description:"S3 Bucket (AWS) Logging Integration"}],s=r.reduce((e,n)=>(e[n.displayName]=n,e),{}),i=r.reduce((e,n)=>(e[n.displayName]=n.id,e),{}),o=r.reduce((e,n)=>(e[n.id]=n.displayName,e),{}),l=e=>e.map(e=>i[e]||e),g=e=>e.map(e=>o[e]||e),u=e=>r.find(n=>n.id===e)}},function(e){e.O(0,[9820,1491,1526,2417,2926,9775,2284,7908,9678,226,8049,6925,2971,2117,1744],function(){return e(e.s=75327)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8021],{40915:function(e,n,r){Promise.resolve().then(r.bind(r,14809))},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return u.Z}});var u=r(20831)},9335:function(e,n,r){"use strict";r.d(n,{JO:function(){return u.Z},OK:function(){return o.Z},nP:function(){return a.Z},td:function(){return t.Z},v0:function(){return l.Z},x4:function(){return i.Z}});var u=r(47323),o=r(12485),l=r(18135),t=r(35242),i=r(29706),a=r(77991)},80443:function(e,n,r){"use strict";var u=r(2265),o=r(99376),l=r(14474),t=r(3914);n.Z=()=>{var e,n,r,i,a,s,d;let c=(0,o.useRouter)(),_="undefined"!=typeof document?(0,t.e)("token"):null;(0,u.useEffect)(()=>{_||c.replace("/sso/key/generate")},[_,c]);let f=(0,u.useMemo)(()=>{if(!_)return null;try{return(0,l.o)(_)}catch(e){return(0,t.b)(),c.replace("/sso/key/generate"),null}},[_,c]);return{token:_,accessToken:null!==(e=null==f?void 0:f.key)&&void 0!==e?e:null,userId:null!==(n=null==f?void 0:f.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==f?void 0:f.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(i=null==f?void 0:f.user_role)&&void 0!==i?i:null),premiumUser:null!==(a=null==f?void 0:f.premium_user)&&void 0!==a?a:null,disabledPersonalKeyCreation:null!==(s=null==f?void 0:f.disabled_non_admin_personal_key_creation)&&void 0!==s?s:null,showSSOBanner:(null==f?void 0:f.login_method)==="username_password"}}},14809:function(e,n,r){"use strict";r.r(n);var u=r(57437),o=r(85809),l=r(80443);n.default=()=>{let{accessToken:e,userRole:n,userId:r}=(0,l.Z)();return(0,u.jsx)(o.Z,{accessToken:e,userRole:n,userID:r,modelData:{}})}},51601:function(e,n,r){"use strict";r.d(n,{p:function(){return o}});var u=r(19250);let o=async e=>{try{let n=await (0,u.modelHubCall)(e);if(console.log("model_info:",n),(null==n?void 0:n.data.length)>0){let e=n.data.map(e=>({model_group:e.model_group,mode:null==e?void 0:e.mode}));return e.sort((e,n)=>e.model_group.localeCompare(n.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}}}},function(e){e.O(0,[9820,1491,1526,2417,2926,9678,7281,6433,1223,901,8049,5809,2971,2117,1744],function(){return e(e.s=40915)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8021],{90165:function(e,n,r){Promise.resolve().then(r.bind(r,14809))},16312:function(e,n,r){"use strict";r.d(n,{z:function(){return u.Z}});var u=r(20831)},9335:function(e,n,r){"use strict";r.d(n,{JO:function(){return u.Z},OK:function(){return o.Z},nP:function(){return a.Z},td:function(){return t.Z},v0:function(){return l.Z},x4:function(){return i.Z}});var u=r(47323),o=r(12485),l=r(18135),t=r(35242),i=r(29706),a=r(77991)},39760:function(e,n,r){"use strict";var u=r(2265),o=r(99376),l=r(14474),t=r(3914);n.Z=()=>{var e,n,r,i,a,s,d;let c=(0,o.useRouter)(),_="undefined"!=typeof document?(0,t.e)("token"):null;(0,u.useEffect)(()=>{_||c.replace("/sso/key/generate")},[_,c]);let f=(0,u.useMemo)(()=>{if(!_)return null;try{return(0,l.o)(_)}catch(e){return(0,t.b)(),c.replace("/sso/key/generate"),null}},[_,c]);return{token:_,accessToken:null!==(e=null==f?void 0:f.key)&&void 0!==e?e:null,userId:null!==(n=null==f?void 0:f.user_id)&&void 0!==n?n:null,userEmail:null!==(r=null==f?void 0:f.user_email)&&void 0!==r?r:null,userRole:function(e){if(!e)return"Undefined Role";switch(e.toLowerCase()){case"app_owner":case"demo_app_owner":return"App Owner";case"app_admin":case"proxy_admin":return"Admin";case"proxy_admin_viewer":return"Admin Viewer";case"org_admin":return"Org Admin";case"internal_user":return"Internal User";case"internal_user_viewer":case"internal_viewer":return"Internal Viewer";case"app_user":return"App User";default:return"Unknown Role"}}(null!==(i=null==f?void 0:f.user_role)&&void 0!==i?i:null),premiumUser:null!==(a=null==f?void 0:f.premium_user)&&void 0!==a?a:null,disabledPersonalKeyCreation:null!==(s=null==f?void 0:f.disabled_non_admin_personal_key_creation)&&void 0!==s?s:null,showSSOBanner:(null==f?void 0:f.login_method)==="username_password"}}},14809:function(e,n,r){"use strict";r.r(n);var u=r(57437),o=r(85809),l=r(39760);n.default=()=>{let{accessToken:e,userRole:n,userId:r}=(0,l.Z)();return(0,u.jsx)(o.Z,{accessToken:e,userRole:n,userID:r,modelData:{}})}},51601:function(e,n,r){"use strict";r.d(n,{p:function(){return o}});var u=r(19250);let o=async e=>{try{let n=await (0,u.modelHubCall)(e);if(console.log("model_info:",n),(null==n?void 0:n.data.length)>0){let e=n.data.map(e=>({model_group:e.model_group,mode:null==e?void 0:e.mode}));return e.sort((e,n)=>e.model_group.localeCompare(n.model_group)),e}return[]}catch(e){throw console.error("Error fetching model info:",e),e}}}},function(e){e.O(0,[9820,1491,1526,2417,2926,9678,7281,6433,1223,901,8049,5809,2971,2117,1744],function(){return e(e.s=90165)}),_N_E=e.O()}]);

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3185],{66407:function(e,t,r){Promise.resolve().then(r.t.bind(r,39974,23)),Promise.resolve().then(r.t.bind(r,2778,23)),Promise.resolve().then(r.bind(r,31857))},99376:function(e,t,r){"use strict";var n=r(35475);r.o(n,"usePathname")&&r.d(t,{usePathname:function(){return n.usePathname}}),r.o(n,"useRouter")&&r.d(t,{useRouter:function(){return n.useRouter}}),r.o(n,"useSearchParams")&&r.d(t,{useSearchParams:function(){return n.useSearchParams}})},31857:function(e,t,r){"use strict";r.d(t,{FeatureFlagsProvider:function(){return i}});var n=r(57437),a=r(2265),u=r(99376);let o=()=>{let e="ui/".replace(/^\/+|\/+$/g,"");return e?"/".concat(e,"/"):"/"},l="feature.refactoredUIFlag",s=(0,a.createContext)(null);function c(e){try{localStorage.setItem(l,String(e))}catch(e){}}let i=e=>{let{children:t}=e,r=(0,u.useRouter)(),[i,f]=(0,a.useState)(()=>(function(){try{let e=localStorage.getItem(l);if(null===e)return localStorage.setItem(l,"false"),!1;let t=e.trim().toLowerCase();if("true"===t||"1"===t)return!0;if("false"===t||"0"===t)return!1;let r=JSON.parse(e);if("boolean"==typeof r)return r;return localStorage.setItem(l,"false"),!1}catch(e){try{localStorage.setItem(l,"false")}catch(e){}return!1}})());return(0,a.useEffect)(()=>{let e=e=>{if(e.key===l&&null!=e.newValue){let t=e.newValue.trim().toLowerCase();f("true"===t||"1"===t)}e.key===l&&null===e.newValue&&(c(!1),f(!1))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)},[]),(0,a.useEffect)(()=>{let e;if(i)return;let t=o();((e=window.location.pathname).endsWith("/")?e:e+"/")!==t&&r.replace(t)},[i,r]),(0,n.jsx)(s.Provider,{value:{refactoredUIFlag:i,setRefactoredUIFlag:e=>{f(e),c(e)}},children:t})};t.Z=()=>{let e=(0,a.useContext)(s);if(!e)throw Error("useFeatureFlags must be used within FeatureFlagsProvider");return e}},2778:function(){},39974:function(e){e.exports={style:{fontFamily:"'__Inter_1c856b', '__Inter_Fallback_1c856b'",fontStyle:"normal"},className:"__className_1c856b"}}},function(e){e.O(0,[1919,2461,2971,2117,1744],function(){return e(e.s=66407)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[3185],{78776:function(e,t,r){Promise.resolve().then(r.t.bind(r,39974,23)),Promise.resolve().then(r.t.bind(r,2778,23)),Promise.resolve().then(r.bind(r,31857))},99376:function(e,t,r){"use strict";var n=r(35475);r.o(n,"usePathname")&&r.d(t,{usePathname:function(){return n.usePathname}}),r.o(n,"useRouter")&&r.d(t,{useRouter:function(){return n.useRouter}}),r.o(n,"useSearchParams")&&r.d(t,{useSearchParams:function(){return n.useSearchParams}})},31857:function(e,t,r){"use strict";r.d(t,{FeatureFlagsProvider:function(){return i}});var n=r(57437),a=r(2265),u=r(99376);let o=()=>{let e="ui/".replace(/^\/+|\/+$/g,"");return e?"/".concat(e,"/"):"/"},l="feature.refactoredUIFlag",s=(0,a.createContext)(null);function c(e){try{localStorage.setItem(l,String(e))}catch(e){}}let i=e=>{let{children:t}=e,r=(0,u.useRouter)(),[i,f]=(0,a.useState)(()=>(function(){try{let e=localStorage.getItem(l);if(null===e)return localStorage.setItem(l,"false"),!1;let t=e.trim().toLowerCase();if("true"===t||"1"===t)return!0;if("false"===t||"0"===t)return!1;let r=JSON.parse(e);if("boolean"==typeof r)return r;return localStorage.setItem(l,"false"),!1}catch(e){try{localStorage.setItem(l,"false")}catch(e){}return!1}})());return(0,a.useEffect)(()=>{let e=e=>{if(e.key===l&&null!=e.newValue){let t=e.newValue.trim().toLowerCase();f("true"===t||"1"===t)}e.key===l&&null===e.newValue&&(c(!1),f(!1))};return window.addEventListener("storage",e),()=>window.removeEventListener("storage",e)},[]),(0,a.useEffect)(()=>{let e;if(i)return;let t=o();((e=window.location.pathname).endsWith("/")?e:e+"/")!==t&&r.replace(t)},[i,r]),(0,n.jsx)(s.Provider,{value:{refactoredUIFlag:i,setRefactoredUIFlag:e=>{f(e),c(e)}},children:t})};t.Z=()=>{let e=(0,a.useContext)(s);if(!e)throw Error("useFeatureFlags must be used within FeatureFlagsProvider");return e}},2778:function(){},39974:function(e){e.exports={style:{fontFamily:"'__Inter_1c856b', '__Inter_Fallback_1c856b'",fontStyle:"normal"},className:"__className_1c856b"}}},function(e){e.O(0,[1919,2461,2971,2117,1744],function(){return e(e.s=78776)}),_N_E=e.O()}]);

View file

@ -1 +1 @@
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8461],{2532:function(e,s,t){Promise.resolve().then(t.bind(t,12011))},12011:function(e,s,t){"use strict";t.r(s),t.d(s,{default:function(){return _}});var r=t(57437),n=t(2265),a=t(99376),l=t(20831),o=t(94789),i=t(12514),c=t(49804),d=t(67101),u=t(84264),m=t(49566),h=t(96761),x=t(84566),g=t(19250),p=t(14474),w=t(13634),f=t(73002),j=t(3914);function _(){let[e]=w.Z.useForm(),s=(0,a.useSearchParams)();(0,j.e)("token");let t=s.get("invitation_id"),_=s.get("action"),[Z,b]=(0,n.useState)(null),[y,k]=(0,n.useState)(""),[S,N]=(0,n.useState)(""),[E,v]=(0,n.useState)(null),[P,U]=(0,n.useState)(""),[C,O]=(0,n.useState)(""),[F,I]=(0,n.useState)(!0);return(0,n.useEffect)(()=>{(0,g.getUiConfig)().then(e=>{console.log("ui config in onboarding.tsx:",e),I(!1)})},[]),(0,n.useEffect)(()=>{t&&!F&&(0,g.getOnboardingCredentials)(t).then(e=>{let s=e.login_url;console.log("login_url:",s),U(s);let t=e.token,r=(0,p.o)(t);O(t),console.log("decoded:",r),b(r.key),console.log("decoded user email:",r.user_email),N(r.user_email),v(r.user_id)})},[t,F]),(0,r.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,r.jsxs)(i.Z,{children:[(0,r.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,r.jsx)(h.Z,{className:"text-xl",children:"reset_password"===_?"Reset Password":"Sign up"}),(0,r.jsx)(u.Z,{children:"reset_password"===_?"Reset your password to access Admin UI.":"Claim your user account to login to Admin UI."}),"reset_password"!==_&&(0,r.jsx)(o.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,r.jsxs)(d.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,r.jsx)(c.Z,{children:"SSO is under the Enterprise Tier."}),(0,r.jsx)(c.Z,{children:(0,r.jsx)(l.Z,{variant:"primary",className:"mb-2",children:(0,r.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,r.jsxs)(w.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",Z,"token:",C,"formValues:",e),Z&&C&&(e.user_email=S,E&&t&&(0,g.claimOnboardingToken)(Z,t,E,e.password).then(e=>{let s="/ui/";s+="?login=success",document.cookie="token="+C,console.log("redirecting to:",s);let t=(0,g.getProxyBaseUrl)();console.log("proxyBaseUrl:",t),t?window.location.href=t+s:window.location.href=s}))},children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(w.Z.Item,{label:"Email Address",name:"user_email",children:(0,r.jsx)(m.Z,{type:"email",disabled:!0,value:S,defaultValue:S,className:"max-w-md"})}),(0,r.jsx)(w.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"reset_password"===_?"Enter your new password":"Create a password for your account",children:(0,r.jsx)(m.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,r.jsx)("div",{className:"mt-10",children:(0,r.jsx)(f.ZP,{htmlType:"submit",children:"reset_password"===_?"Reset Password":"Sign Up"})})]})]})})}}},function(e){e.O(0,[3665,9820,1491,1526,8806,8049,2971,2117,1744],function(){return e(e.s=2532)}),_N_E=e.O()}]);
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[8461],{8672:function(e,s,t){Promise.resolve().then(t.bind(t,12011))},12011:function(e,s,t){"use strict";t.r(s),t.d(s,{default:function(){return _}});var r=t(57437),n=t(2265),a=t(99376),l=t(20831),o=t(94789),i=t(12514),c=t(49804),d=t(67101),u=t(84264),m=t(49566),h=t(96761),x=t(84566),g=t(19250),p=t(14474),w=t(13634),f=t(73002),j=t(3914);function _(){let[e]=w.Z.useForm(),s=(0,a.useSearchParams)();(0,j.e)("token");let t=s.get("invitation_id"),_=s.get("action"),[Z,b]=(0,n.useState)(null),[y,k]=(0,n.useState)(""),[S,N]=(0,n.useState)(""),[E,v]=(0,n.useState)(null),[P,U]=(0,n.useState)(""),[C,O]=(0,n.useState)(""),[F,I]=(0,n.useState)(!0);return(0,n.useEffect)(()=>{(0,g.getUiConfig)().then(e=>{console.log("ui config in onboarding.tsx:",e),I(!1)})},[]),(0,n.useEffect)(()=>{t&&!F&&(0,g.getOnboardingCredentials)(t).then(e=>{let s=e.login_url;console.log("login_url:",s),U(s);let t=e.token,r=(0,p.o)(t);O(t),console.log("decoded:",r),b(r.key),console.log("decoded user email:",r.user_email),N(r.user_email),v(r.user_id)})},[t,F]),(0,r.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,r.jsxs)(i.Z,{children:[(0,r.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,r.jsx)(h.Z,{className:"text-xl",children:"reset_password"===_?"Reset Password":"Sign up"}),(0,r.jsx)(u.Z,{children:"reset_password"===_?"Reset your password to access Admin UI.":"Claim your user account to login to Admin UI."}),"reset_password"!==_&&(0,r.jsx)(o.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,r.jsxs)(d.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,r.jsx)(c.Z,{children:"SSO is under the Enterprise Tier."}),(0,r.jsx)(c.Z,{children:(0,r.jsx)(l.Z,{variant:"primary",className:"mb-2",children:(0,r.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,r.jsxs)(w.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",Z,"token:",C,"formValues:",e),Z&&C&&(e.user_email=S,E&&t&&(0,g.claimOnboardingToken)(Z,t,E,e.password).then(e=>{let s="/ui/";s+="?login=success",document.cookie="token="+C,console.log("redirecting to:",s);let t=(0,g.getProxyBaseUrl)();console.log("proxyBaseUrl:",t),t?window.location.href=t+s:window.location.href=s}))},children:[(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(w.Z.Item,{label:"Email Address",name:"user_email",children:(0,r.jsx)(m.Z,{type:"email",disabled:!0,value:S,defaultValue:S,className:"max-w-md"})}),(0,r.jsx)(w.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"reset_password"===_?"Enter your new password":"Create a password for your account",children:(0,r.jsx)(m.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,r.jsx)("div",{className:"mt-10",children:(0,r.jsx)(f.ZP,{htmlType:"submit",children:"reset_password"===_?"Reset Password":"Sign Up"})})]})]})})}}},function(e){e.O(0,[3665,9820,1491,1526,8806,8049,2971,2117,1744],function(){return e(e.s=8672)}),_N_E=e.O()}]);

Some files were not shown because too many files have changed in this diff Show more