mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
Merge branch 'BerriAI:main' into main
This commit is contained in:
commit
d7c3306c7a
209 changed files with 6118 additions and 798 deletions
|
|
@ -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
|
||||
|
|
|
|||
1
.github/workflows/interpret_load_test.py
vendored
1
.github/workflows/interpret_load_test.py
vendored
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
},
|
||||
}'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
:::
|
||||
|
|
|
|||
|
|
@ -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**
|
||||
|
||||
|
||||
|
|
|
|||
68
docs/my-website/docs/providers/azure/azure_speech.md
Normal file
68
docs/my-website/docs/providers/azure/azure_speech.md
Normal 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>`
|
||||
212
docs/my-website/docs/providers/azure_ai_speech.md
Normal file
212
docs/my-website/docs/providers/azure_ai_speech.md
Normal 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)
|
||||
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 Python’s 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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
```
|
||||
|
|
@ -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
|
||||
|
|
@ -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 pod’s 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
|
||||
|
|
|
|||
|
|
@ -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) |
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
---
|
||||
|
|
|
|||
300
docs/my-website/release_notes/v1.78.5-stable/index.md
Normal file
300
docs/my-website/release_notes/v1.78.5-stable/index.md
Normal 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)**
|
||||
|
||||
|
|
@ -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",
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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/",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ###########
|
||||
#############################################
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
8
litellm/llms/azure/text_to_speech/__init__.py
Normal file
8
litellm/llms/azure/text_to_speech/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
"""Azure Text-to-Speech module"""
|
||||
|
||||
from .transformation import AzureAVATextToSpeechConfig
|
||||
|
||||
__all__ = [
|
||||
"AzureAVATextToSpeechConfig",
|
||||
]
|
||||
|
||||
373
litellm/llms/azure/text_to_speech/transformation.py
Normal file
373
litellm/llms/azure/text_to_speech/transformation.py
Normal 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("&", "&")
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace('"', """)
|
||||
.replace("'", "'")
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
147
litellm/llms/base_llm/text_to_speech/transformation.py
Normal file
147
litellm/llms/base_llm/text_to_speech/transformation.py
Normal 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,
|
||||
)
|
||||
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -158,6 +158,7 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig):
|
|||
"parallel_tool_calls",
|
||||
"audio",
|
||||
"web_search_options",
|
||||
"service_tier",
|
||||
"safety_identifier",
|
||||
] # works across all models
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
133
litellm/main.py
133
litellm/main.py
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
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
|
|
@ -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()}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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()}]);
|
||||
|
|
@ -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()}]);
|
||||
|
|
@ -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()}]);
|
||||
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
|
|
@ -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()}]);
|
||||
|
|
@ -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()}]);
|
||||
|
|
@ -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
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
|
|
@ -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()}]);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -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()}]);
|
||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue