mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge branch 'BerriAI:main' into main
This commit is contained in:
commit
5a6f494d0f
196 changed files with 4065 additions and 2698 deletions
|
|
@ -69,6 +69,7 @@ jobs:
|
|||
pip install "Pillow==10.3.0"
|
||||
pip install "jsonschema==4.22.0"
|
||||
pip install "pytest-xdist==3.6.1"
|
||||
pip install "websockets==10.4"
|
||||
- save_cache:
|
||||
paths:
|
||||
- ./venv
|
||||
|
|
|
|||
1
.github/workflows/reset_stable.yml
vendored
1
.github/workflows/reset_stable.yml
vendored
|
|
@ -2,6 +2,7 @@ name: Reset litellm_stable branch
|
|||
|
||||
on:
|
||||
release:
|
||||
types: [published, created]
|
||||
jobs:
|
||||
update-stable-branch:
|
||||
if: ${{ startsWith(github.event.release.tag_name, 'v') && !endsWith(github.event.release.tag_name, '-stable') }}
|
||||
|
|
|
|||
|
|
@ -112,4 +112,5 @@ transcript = client.audio.transcriptions.create(
|
|||
- OpenAI
|
||||
- Azure
|
||||
- [Fireworks AI](./providers/fireworks_ai.md#audio-transcription)
|
||||
- [Groq](./providers/groq.md#speech-to-text---whisper)
|
||||
- [Groq](./providers/groq.md#speech-to-text---whisper)
|
||||
- [Deepgram](./providers/deepgram.md)
|
||||
|
|
@ -394,6 +394,32 @@ print(response)
|
|||
|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| mistral-embed | `embedding(model="mistral/mistral-embed", input)` |
|
||||
|
||||
## Gemini AI Embedding Models
|
||||
|
||||
### API keys
|
||||
|
||||
This can be set as env variables or passed as **params to litellm.embedding()**
|
||||
```python
|
||||
import os
|
||||
os.environ["GEMINI_API_KEY"] = ""
|
||||
```
|
||||
|
||||
### Usage - Embedding
|
||||
```python
|
||||
from litellm import embedding
|
||||
response = embedding(
|
||||
model="gemini/text-embedding-004",
|
||||
input=["good morning from litellm"],
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
All models listed [here](https://ai.google.dev/gemini-api/docs/models/gemini) are supported:
|
||||
|
||||
| Model Name | Function Call |
|
||||
| :--- | :--- |
|
||||
| text-embedding-004 | `embedding(model="gemini/text-embedding-004", input)` |
|
||||
|
||||
|
||||
## Vertex AI Embedding Models
|
||||
|
||||
|
|
@ -411,7 +437,7 @@ response = embedding(
|
|||
print(response)
|
||||
```
|
||||
|
||||
## Supported Models
|
||||
### Supported Models
|
||||
All models listed [here](https://github.com/BerriAI/litellm/blob/57f37f743886a0249f630a6792d49dffc2c5d9b7/model_prices_and_context_window.json#L835) are supported
|
||||
|
||||
| Model Name | Function Call |
|
||||
|
|
@ -509,4 +535,4 @@ curl -X POST 'http://0.0.0.0:4000/v1/embeddings' \
|
|||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
</Tabs>
|
||||
|
|
|
|||
87
docs/my-website/docs/providers/deepgram.md
Normal file
87
docs/my-website/docs/providers/deepgram.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Deepgram
|
||||
|
||||
LiteLLM supports Deepgram's `/listen` endpoint.
|
||||
|
||||
| Property | Details |
|
||||
|-------|-------|
|
||||
| Description | Deepgram's voice AI platform provides APIs for speech-to-text, text-to-speech, and language understanding. |
|
||||
| Provider Route on LiteLLM | `deepgram/` |
|
||||
| Provider Doc | [Deepgram ↗](https://developers.deepgram.com/docs/introduction) |
|
||||
| Supported OpenAI Endpoints | `/audio/transcriptions` |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from litellm import transcription
|
||||
import os
|
||||
|
||||
# set api keys
|
||||
os.environ["DEEPGRAM_API_KEY"] = ""
|
||||
audio_file = open("/path/to/audio.mp3", "rb")
|
||||
|
||||
response = transcription(model="deepgram/nova-2", file=audio_file)
|
||||
|
||||
print(f"response: {response}")
|
||||
```
|
||||
|
||||
## LiteLLM Proxy Usage
|
||||
|
||||
### Add model to config
|
||||
|
||||
1. Add model to config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: nova-2
|
||||
litellm_params:
|
||||
model: deepgram/nova-2
|
||||
api_key: os.environ/DEEPGRAM_API_KEY
|
||||
model_info:
|
||||
mode: audio_transcription
|
||||
|
||||
general_settings:
|
||||
master_key: sk-1234
|
||||
```
|
||||
|
||||
### Start proxy
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="curl" label="Curl">
|
||||
|
||||
```bash
|
||||
curl --location 'http://0.0.0.0:4000/v1/audio/transcriptions' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--form 'file=@"/Users/krrishdholakia/Downloads/gettysburg.wav"' \
|
||||
--form 'model="nova-2"'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
|
||||
audio_file = open("speech.mp3", "rb")
|
||||
transcript = client.audio.transcriptions.create(
|
||||
model="nova-2",
|
||||
file=audio_file
|
||||
)
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
@ -190,6 +190,116 @@ print(response)
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Document Inlining
|
||||
|
||||
LiteLLM supports document inlining for Fireworks AI models. This is useful for models that are not vision models, but still need to parse documents/images/etc.
|
||||
|
||||
LiteLLM will add `#transform=inline` to the url of the image_url, if the model is not a vision model.[**See Code**](https://github.com/BerriAI/litellm/blob/1ae9d45798bdaf8450f2dfdec703369f3d2212b7/litellm/llms/fireworks_ai/chat/transformation.py#L114)
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["FIREWORKS_AI_API_KEY"] = "YOUR_API_KEY"
|
||||
os.environ["FIREWORKS_AI_API_BASE"] = "https://audio-prod.us-virginia-1.direct.fireworks.ai/v1"
|
||||
|
||||
completion = litellm.completion(
|
||||
model="fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://storage.googleapis.com/fireworks-public/test/sample_resume.pdf"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "What are the candidate's BA and MBA GPAs?",
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
print(completion)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
1. Setup config.yaml
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: llama-v3p3-70b-instruct
|
||||
litellm_params:
|
||||
model: fireworks_ai/accounts/fireworks/models/llama-v3p3-70b-instruct
|
||||
api_key: os.environ/FIREWORKS_AI_API_KEY
|
||||
# api_base: os.environ/FIREWORKS_AI_API_BASE [OPTIONAL], defaults to "https://api.fireworks.ai/inference/v1"
|
||||
```
|
||||
|
||||
2. Start Proxy
|
||||
|
||||
```
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
3. Test it
|
||||
|
||||
```bash
|
||||
curl -L -X POST 'http://0.0.0.0:4000/chat/completions' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Authorization: Bearer YOUR_API_KEY' \
|
||||
-d '{"model": "llama-v3p3-70b-instruct",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://storage.googleapis.com/fireworks-public/test/sample_resume.pdf"
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "What are the candidate's BA and MBA GPAs?",
|
||||
},
|
||||
],
|
||||
}
|
||||
]}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Disable Auto-add
|
||||
|
||||
If you want to disable the auto-add of `#transform=inline` to the url of the image_url, you can set the `auto_add_transform_inline` to `False` in the `FireworksAIConfig` class.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
litellm.disable_add_transform_inline_image_block = True
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
disable_add_transform_inline_image_block: true
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Supported Models - ALL Fireworks AI Models Supported!
|
||||
|
||||
:::info
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import TabItem from '@theme/TabItem';
|
|||
| Provider Route on LiteLLM | `gemini/` |
|
||||
| Provider Doc | [Google AI Studio ↗](https://ai.google.dev/aistudio) |
|
||||
| API Endpoint for Provider | https://generativelanguage.googleapis.com |
|
||||
| Supported OpenAI Endpoints | `/chat/completions`, `/embeddings`, `/completions` |
|
||||
| Supported OpenAI Endpoints | `/chat/completions`, [`/embeddings`](../embedding/supported_embedding#gemini-ai-embedding-models), `/completions` |
|
||||
| Pass-through Endpoint | [Supported](../pass_through/google_ai_studio.md) |
|
||||
|
||||
<br />
|
||||
|
|
|
|||
284
docs/my-website/docs/proxy/clientside_auth.md
Normal file
284
docs/my-website/docs/proxy/clientside_auth.md
Normal file
|
|
@ -0,0 +1,284 @@
|
|||
# Clientside LLM Credentials
|
||||
|
||||
|
||||
### Pass User LLM API Keys, Fallbacks
|
||||
Allow your end-users to pass their model list, api base, OpenAI API key (any LiteLLM supported provider) to make requests
|
||||
|
||||
**Note** This is not related to [virtual keys](./virtual_keys.md). This is for when you want to pass in your users actual LLM API keys.
|
||||
|
||||
:::info
|
||||
|
||||
**You can pass a litellm.RouterConfig as `user_config`, See all supported params here https://github.com/BerriAI/litellm/blob/main/litellm/types/router.py **
|
||||
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="openai-py" label="OpenAI Python">
|
||||
|
||||
#### Step 1: Define user model list & config
|
||||
```python
|
||||
import os
|
||||
|
||||
user_config = {
|
||||
'model_list': [
|
||||
{
|
||||
'model_name': 'user-azure-instance',
|
||||
'litellm_params': {
|
||||
'model': 'azure/chatgpt-v-2',
|
||||
'api_key': os.getenv('AZURE_API_KEY'),
|
||||
'api_version': os.getenv('AZURE_API_VERSION'),
|
||||
'api_base': os.getenv('AZURE_API_BASE'),
|
||||
'timeout': 10,
|
||||
},
|
||||
'tpm': 240000,
|
||||
'rpm': 1800,
|
||||
},
|
||||
{
|
||||
'model_name': 'user-openai-instance',
|
||||
'litellm_params': {
|
||||
'model': 'gpt-3.5-turbo',
|
||||
'api_key': os.getenv('OPENAI_API_KEY'),
|
||||
'timeout': 10,
|
||||
},
|
||||
'tpm': 240000,
|
||||
'rpm': 1800,
|
||||
},
|
||||
],
|
||||
'num_retries': 2,
|
||||
'allowed_fails': 3,
|
||||
'fallbacks': [
|
||||
{
|
||||
'user-azure-instance': ['user-openai-instance']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### Step 2: Send user_config in `extra_body`
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# send request to `user-azure-instance`
|
||||
response = client.chat.completions.create(model="user-azure-instance", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={
|
||||
"user_config": user_config
|
||||
}
|
||||
) # 👈 User config
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="openai-js" label="OpenAI JS">
|
||||
|
||||
#### Step 1: Define user model list & config
|
||||
```javascript
|
||||
const os = require('os');
|
||||
|
||||
const userConfig = {
|
||||
model_list: [
|
||||
{
|
||||
model_name: 'user-azure-instance',
|
||||
litellm_params: {
|
||||
model: 'azure/chatgpt-v-2',
|
||||
api_key: process.env.AZURE_API_KEY,
|
||||
api_version: process.env.AZURE_API_VERSION,
|
||||
api_base: process.env.AZURE_API_BASE,
|
||||
timeout: 10,
|
||||
},
|
||||
tpm: 240000,
|
||||
rpm: 1800,
|
||||
},
|
||||
{
|
||||
model_name: 'user-openai-instance',
|
||||
litellm_params: {
|
||||
model: 'gpt-3.5-turbo',
|
||||
api_key: process.env.OPENAI_API_KEY,
|
||||
timeout: 10,
|
||||
},
|
||||
tpm: 240000,
|
||||
rpm: 1800,
|
||||
},
|
||||
],
|
||||
num_retries: 2,
|
||||
allowed_fails: 3,
|
||||
fallbacks: [
|
||||
{
|
||||
'user-azure-instance': ['user-openai-instance']
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
#### Step 2: Send `user_config` as a param to `openai.chat.completions.create`
|
||||
|
||||
```javascript
|
||||
const { OpenAI } = require('openai');
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: "sk-1234",
|
||||
baseURL: "http://0.0.0.0:4000"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: 'user', content: 'Say this is a test' }],
|
||||
model: 'gpt-3.5-turbo',
|
||||
user_config: userConfig // # 👈 User config
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Pass User LLM API Keys / API Base
|
||||
Allows your users to pass in their OpenAI API key/API base (any LiteLLM supported provider) to make requests
|
||||
|
||||
Here's how to do it:
|
||||
|
||||
#### 1. Enable configurable clientside auth credentials for a provider
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "fireworks_ai/*"
|
||||
litellm_params:
|
||||
model: "fireworks_ai/*"
|
||||
configurable_clientside_auth_params: ["api_base"]
|
||||
# OR
|
||||
configurable_clientside_auth_params: [{"api_base": "^https://litellm.*direct\.fireworks\.ai/v1$"}] # 👈 regex
|
||||
```
|
||||
|
||||
Specify any/all auth params you want the user to be able to configure:
|
||||
|
||||
- api_base (✅ regex supported)
|
||||
- api_key
|
||||
- base_url
|
||||
|
||||
(check [provider docs](../providers/) for provider-specific auth params - e.g. `vertex_project`)
|
||||
|
||||
|
||||
#### 2. Test it!
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={"api_key": "my-bad-key", "api_base": "https://litellm-dev.direct.fireworks.ai/v1"}) # 👈 clientside credentials
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
More examples:
|
||||
<Tabs>
|
||||
<TabItem value="openai-py" label="Azure Credentials">
|
||||
|
||||
Pass in the litellm_params (E.g. api_key, api_base, etc.) via the `extra_body` parameter in the OpenAI client.
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={
|
||||
"api_key": "my-azure-key",
|
||||
"api_base": "my-azure-base",
|
||||
"api_version": "my-azure-version"
|
||||
}) # 👈 User Key
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai-js" label="OpenAI JS">
|
||||
|
||||
For JS, the OpenAI client accepts passing params in the `create(..)` body as normal.
|
||||
|
||||
```javascript
|
||||
const { OpenAI } = require('openai');
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: "sk-1234",
|
||||
baseURL: "http://0.0.0.0:4000"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: 'user', content: 'Say this is a test' }],
|
||||
model: 'gpt-3.5-turbo',
|
||||
api_key: "my-bad-key" // 👈 User Key
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Pass provider-specific params (e.g. Region, Project ID, etc.)
|
||||
|
||||
Specify the region, project id, etc. to use for making requests to Vertex AI on the clientside.
|
||||
|
||||
Any value passed in the Proxy's request body, will be checked by LiteLLM against the mapped openai / litellm auth params.
|
||||
|
||||
Unmapped params, will be assumed to be provider-specific params, and will be passed through to the provider in the LLM API's request body.
|
||||
|
||||
```bash
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="anything",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={ # pass any additional litellm_params here
|
||||
vertex_ai_location: "us-east1"
|
||||
}
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
|
@ -138,6 +138,7 @@ general_settings:
|
|||
| disable_end_user_cost_tracking | boolean | If true, turns off end user cost tracking on prometheus metrics + litellm spend logs table on proxy. |
|
||||
| disable_end_user_cost_tracking_prometheus_only | boolean | If true, turns off end user cost tracking on prometheus metrics only. |
|
||||
| key_generation_settings | object | Restricts who can generate keys. [Further docs](./virtual_keys.md#restricting-key-generation) |
|
||||
| disable_add_transform_inline_image_block | boolean | For Fireworks AI models - if true, turns off the auto-add of `#transform=inline` to the url of the image_url, if the model is not a vision model. |
|
||||
|
||||
### general_settings - Reference
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,14 @@ guardrails:
|
|||
mode: "post_call"
|
||||
api_key: os.environ/APORIA_API_KEY_2
|
||||
api_base: os.environ/APORIA_API_BASE_2
|
||||
guardrail_info: # Optional field, info is returned on GET /guardrails/list
|
||||
# you can enter any fields under info for consumers of your guardrail
|
||||
params:
|
||||
- name: "toxicity_score"
|
||||
type: "float"
|
||||
description: "Score between 0-1 indicating content toxicity level"
|
||||
- name: "pii_detection"
|
||||
type: "boolean"
|
||||
```
|
||||
|
||||
|
||||
|
|
@ -113,18 +121,132 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
</Tabs>
|
||||
|
||||
|
||||
## Advanced
|
||||
## **Using Guardrails Client Side**
|
||||
|
||||
### ✨ Pass additional parameters to guardrail
|
||||
### Test yourself **(OSS)**
|
||||
|
||||
Pass `guardrails` to your request body to test it
|
||||
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-npnwjPQciVRok5yNZgKmFQ" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{"role": "user", "content": "hi my email is ishaan@berri.ai"}
|
||||
],
|
||||
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
|
||||
}'
|
||||
```
|
||||
|
||||
### Expose to your users **(Enterprise)**
|
||||
|
||||
Follow this simple workflow to implement and tune guardrails:
|
||||
|
||||
### 1. ✨ View Available Guardrails
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
✨ This is an Enterprise only feature [Get a free trial](https://www.litellm.ai/#trial)
|
||||
|
||||
:::
|
||||
|
||||
First, check what guardrails are available and their parameters:
|
||||
|
||||
|
||||
Call `/guardrails/list` to view available guardrails and the guardrail info (supported parameters, description, etc)
|
||||
|
||||
```shell
|
||||
curl -X GET 'http://0.0.0.0:4000/guardrails/list'
|
||||
```
|
||||
|
||||
Expected response
|
||||
|
||||
```json
|
||||
{
|
||||
"guardrails": [
|
||||
{
|
||||
"guardrail_name": "aporia-post-guard",
|
||||
"guardrail_info": {
|
||||
"params": [
|
||||
{
|
||||
"name": "toxicity_score",
|
||||
"type": "float",
|
||||
"description": "Score between 0-1 indicating content toxicity level"
|
||||
},
|
||||
{
|
||||
"name": "pii_detection",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
>
|
||||
This config will return the `/guardrails/list` response above. The `guardrail_info` field is optional and you can add any fields under info for consumers of your guardrail
|
||||
>
|
||||
```yaml
|
||||
- guardrail_name: "aporia-post-guard"
|
||||
litellm_params:
|
||||
guardrail: aporia # supported values: "aporia", "lakera"
|
||||
mode: "post_call"
|
||||
api_key: os.environ/APORIA_API_KEY_2
|
||||
api_base: os.environ/APORIA_API_BASE_2
|
||||
guardrail_info: # Optional field, info is returned on GET /guardrails/list
|
||||
# you can enter any fields under info for consumers of your guardrail
|
||||
params:
|
||||
- name: "toxicity_score"
|
||||
type: "float"
|
||||
description: "Score between 0-1 indicating content toxicity level"
|
||||
- name: "pii_detection"
|
||||
type: "boolean"
|
||||
```
|
||||
|
||||
|
||||
### 2. Apply Guardrails
|
||||
Add selected guardrails to your chat completion request:
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [{"role": "user", "content": "your message"}],
|
||||
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
|
||||
}'
|
||||
```
|
||||
|
||||
### 3. Test with Mock LLM completions
|
||||
|
||||
Send `mock_response` to test guardrails without making an LLM call. More info on `mock_response` [here](../../completion/mock_requests)
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-npnwjPQciVRok5yNZgKmFQ" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{"role": "user", "content": "hi my email is ishaan@berri.ai"}
|
||||
],
|
||||
"mock_response": "This is a mock response",
|
||||
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
### 4. ✨ Pass Dynamic Parameters to Guardrail
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get a free trial](https://www.litellm.ai/#trial)
|
||||
|
||||
:::
|
||||
|
||||
Use this to pass additional parameters to the guardrail API call. e.g. things like success threshold. **[See `guardrails` spec for more details](#spec-guardrails-parameter)**
|
||||
|
||||
|
||||
|
|
@ -196,11 +318,42 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
</Tabs>
|
||||
|
||||
|
||||
|
||||
|
||||
## **Proxy Admin Controls**
|
||||
|
||||
### ✨ Monitoring Guardrails
|
||||
|
||||
Monitor which guardrails were executed and whether they passed or failed. e.g. guardrail going rogue and failing requests we don't intend to fail
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get a free trial](https://www.litellm.ai/#trial)
|
||||
|
||||
:::
|
||||
|
||||
#### Setup
|
||||
|
||||
1. Connect LiteLLM to a [supported logging provider](../logging)
|
||||
2. Make a request with a `guardrails` parameter
|
||||
3. Check your logging provider for the guardrail trace
|
||||
|
||||
#### Traced Guardrail Success
|
||||
|
||||
<Image img={require('../../../img/gd_success.png')} />
|
||||
|
||||
#### Traced Guardrail Failure
|
||||
|
||||
<Image img={require('../../../img/gd_fail.png')} />
|
||||
|
||||
|
||||
|
||||
|
||||
### ✨ Control Guardrails per Project (API Key)
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
✨ This is an Enterprise only feature [Get a free trial](https://www.litellm.ai/#trial)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -262,7 +415,7 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
✨ This is an Enterprise only feature [Get a free trial](https://www.litellm.ai/#trial)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -319,28 +472,27 @@ The `pii_masking` guardrail ran on this request because api key=sk-jNm1Zar7XfNdZ
|
|||
:::
|
||||
|
||||
|
||||
## Specification
|
||||
|
||||
### ✨ List guardrails
|
||||
### `guardrails` Configuration on YAML
|
||||
|
||||
Show available guardrails on the proxy server. This makes it easier for developers to know what guardrails are available / can be used.
|
||||
|
||||
```shell
|
||||
curl -X GET 'http://0.0.0.0:4000/guardrails/list'
|
||||
```yaml
|
||||
guardrails:
|
||||
- guardrail_name: string # Required: Name of the guardrail
|
||||
litellm_params: # Required: Configuration parameters
|
||||
guardrail: string # Required: One of "aporia", "bedrock", "guardrails_ai", "lakera", "presidio", "hide-secrets"
|
||||
mode: string # Required: One of "pre_call", "post_call", "during_call", "logging_only"
|
||||
api_key: string # Required: API key for the guardrail service
|
||||
api_base: string # Optional: Base URL for the guardrail service
|
||||
guardrail_info: # Optional[Dict]: Additional information about the guardrail
|
||||
|
||||
```
|
||||
|
||||
Expected response
|
||||
|
||||
```json
|
||||
{
|
||||
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
|
||||
}
|
||||
```
|
||||
|
||||
## Spec: `guardrails` Parameter
|
||||
### `guardrails` Request Parameter
|
||||
|
||||
The `guardrails` parameter can be passed to any LiteLLM Proxy endpoint (`/chat/completions`, `/completions`, `/embeddings`).
|
||||
|
||||
### Format Options
|
||||
#### Format Options
|
||||
|
||||
1. Simple List Format:
|
||||
```python
|
||||
|
|
@ -364,7 +516,7 @@ In this format the dictionary key is `guardrail_name` you want to run
|
|||
}
|
||||
```
|
||||
|
||||
### Type Definition
|
||||
#### Type Definition
|
||||
```python
|
||||
guardrails: Union[
|
||||
List[str], # Simple list of guardrail names
|
||||
|
|
|
|||
|
|
@ -168,6 +168,20 @@ Expected Response
|
|||
}
|
||||
```
|
||||
|
||||
### Realtime Models
|
||||
|
||||
To run realtime health checks, specify the mode as "realtime" in your config for the relevant model.
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: openai/gpt-4o-realtime-audio
|
||||
litellm_params:
|
||||
model: openai/gpt-4o-realtime-audio
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
model_info:
|
||||
mode: realtime
|
||||
```
|
||||
|
||||
## Background Health Checks
|
||||
|
||||
You can enable model health checks being run in the background, to prevent each model from being queried too frequently via `/health`.
|
||||
|
|
|
|||
346
docs/my-website/docs/proxy/model_access.md
Normal file
346
docs/my-website/docs/proxy/model_access.md
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Control Model Access
|
||||
|
||||
## **Restrict models by Virtual Key**
|
||||
|
||||
Set allowed models for a key using the `models` param
|
||||
|
||||
|
||||
```shell
|
||||
curl 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"models": ["gpt-3.5-turbo", "gpt-4"]}'
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
This key can only make requests to `models` that are `gpt-3.5-turbo` or `gpt-4`
|
||||
|
||||
:::
|
||||
|
||||
Verify this is set correctly by
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `models` for the key generated
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
### [API Reference](https://litellm-api.up.railway.app/#/key%20management/generate_key_fn_key_generate_post)
|
||||
|
||||
## **Restrict models by `team_id`**
|
||||
`litellm-dev` can only access `azure-gpt-3.5`
|
||||
|
||||
**1. Create a team via `/team/new`**
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/team/new' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"team_alias": "litellm-dev",
|
||||
"models": ["azure-gpt-3.5"]
|
||||
}'
|
||||
|
||||
# returns {...,"team_id": "my-unique-id"}
|
||||
```
|
||||
|
||||
**2. Create a key for team**
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"team_id": "my-unique-id"}'
|
||||
```
|
||||
|
||||
**3. Test it**
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer sk-qo992IjKOC2CHKZGRoJIGA' \
|
||||
--data '{
|
||||
"model": "BEDROCK_GROUP",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
```shell
|
||||
{"error":{"message":"Invalid model for team litellm-dev: BEDROCK_GROUP. Valid models for team are: ['azure-gpt-3.5']\n\n\nTraceback (most recent call last):\n File \"/Users/ishaanjaffer/Github/litellm/litellm/proxy/proxy_server.py\", line 2298, in chat_completion\n _is_valid_team_configs(\n File \"/Users/ishaanjaffer/Github/litellm/litellm/proxy/utils.py\", line 1296, in _is_valid_team_configs\n raise Exception(\nException: Invalid model for team litellm-dev: BEDROCK_GROUP. Valid models for team are: ['azure-gpt-3.5']\n\n","type":"None","param":"None","code":500}}%
|
||||
```
|
||||
|
||||
### [API Reference](https://litellm-api.up.railway.app/#/team%20management/new_team_team_new_post)
|
||||
|
||||
|
||||
## **Model Access Groups**
|
||||
|
||||
Use model access groups to give users access to select models, and add new ones to it over time (e.g. mistral, llama-2, etc.)
|
||||
|
||||
**Step 1. Assign model, access group in config.yaml**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
model_info:
|
||||
access_groups: ["beta-models"] # 👈 Model Access Group
|
||||
- model_name: fireworks-llama-v3-70b-instruct
|
||||
litellm_params:
|
||||
model: fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct
|
||||
api_key: "os.environ/FIREWORKS"
|
||||
model_info:
|
||||
access_groups: ["beta-models"] # 👈 Model Access Group
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="key" label="Key Access Groups">
|
||||
|
||||
**Create key with access group**
|
||||
|
||||
```bash
|
||||
curl --location 'http://localhost:4000/key/generate' \
|
||||
-H 'Authorization: Bearer <your-master-key>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"models": ["beta-models"], # 👈 Model Access Group
|
||||
"max_budget": 0,}'
|
||||
```
|
||||
|
||||
Test Key
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `beta-models` access group
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="team" label="Team Access Groups">
|
||||
|
||||
Create Team
|
||||
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/team/new' \
|
||||
-H 'Authorization: Bearer sk-<key-from-previous-step>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"models": ["beta-models"]}'
|
||||
```
|
||||
|
||||
Create Key for Team
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-<key-from-previous-step>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"team_id": "0ac97648-c194-4c90-8cd6-40af7b0d2d2a"}
|
||||
```
|
||||
|
||||
|
||||
Test Key
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `beta-models` access group
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
### ✨ Control Access on Wildcard Models
|
||||
|
||||
Control access to all models with a specific prefix (e.g. `openai/*`).
|
||||
|
||||
Use this to also give users access to all models, except for a few that you don't want them to use (e.g. `openai/o1-*`).
|
||||
|
||||
:::info
|
||||
|
||||
Setting model access groups on wildcard models is an Enterprise feature.
|
||||
|
||||
See pricing [here](https://litellm.ai/#pricing)
|
||||
|
||||
Get a trial key [here](https://litellm.ai/#trial)
|
||||
:::
|
||||
|
||||
|
||||
1. Setup config.yaml
|
||||
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: openai/*
|
||||
litellm_params:
|
||||
model: openai/*
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
model_info:
|
||||
access_groups: ["default-models"]
|
||||
- model_name: openai/o1-*
|
||||
litellm_params:
|
||||
model: openai/o1-*
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
model_info:
|
||||
access_groups: ["restricted-models"]
|
||||
```
|
||||
|
||||
2. Generate a key with access to `default-models`
|
||||
|
||||
```bash
|
||||
curl -L -X POST 'http://0.0.0.0:4000/key/generate' \
|
||||
-H 'Authorization: Bearer sk-1234' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"models": ["default-models"],
|
||||
}'
|
||||
```
|
||||
|
||||
3. Test the key
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Successful Request" value = "success">
|
||||
|
||||
```bash
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "openai/gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="bad-request" label="Rejected Request">
|
||||
|
||||
```bash
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "openai/o1-mini",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
32
docs/my-website/docs/proxy/spending_monitoring.md
Normal file
32
docs/my-website/docs/proxy/spending_monitoring.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Using at Scale (1M+ rows in DB)
|
||||
|
||||
This document is a guide for using LiteLLM Proxy once you have crossed 1M+ rows in the LiteLLM Spend Logs Database.
|
||||
|
||||
<iframe width="840" height="500" src="https://www.loom.com/embed/eafd90d5374d4633b99c441fb04df351" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
|
||||
|
||||
## Why is UI Usage Tracking disabled?
|
||||
- Heavy database queries on `LiteLLM_Spend_Logs` (once it has 1M+ rows) can slow down your LLM API requests. **We do not want this happening**
|
||||
|
||||
## Solutions for Usage Tracking
|
||||
|
||||
Step 1. **Export Logs to Cloud Storage**
|
||||
- [Send logs to S3, GCS, or Azure Blob Storage](https://docs.litellm.ai/docs/proxy/logging)
|
||||
- [Log format specification](https://docs.litellm.ai/docs/proxy/logging_spec)
|
||||
|
||||
Step 2. **Analyze Data**
|
||||
- Use tools like [Redash](https://redash.io/), [Databricks](https://www.databricks.com/), [Snowflake](https://www.snowflake.com/en/) to analyze exported logs
|
||||
|
||||
[Optional] Step 3. **Disable Spend + Error Logs to LiteLLM DB**
|
||||
|
||||
[See Instructions Here](./prod#6-disable-spend_logs--error_logs-if-not-using-the-litellm-ui)
|
||||
|
||||
Disabling this will prevent your LiteLLM DB from growing in size, which will help with performance (prevent health checks from failing).
|
||||
|
||||
## Need an Integration? Get in Touch
|
||||
|
||||
- Request a logging integration on [Github Issues](https://github.com/BerriAI/litellm/issues)
|
||||
- Get in [touch with LiteLLM Founders](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat)
|
||||
- Get a 7-day free trial of LiteLLM [here](https://litellm.ai#trial)
|
||||
|
||||
|
||||
|
||||
|
|
@ -996,254 +996,3 @@ Get a list of responses when `model` is passed as a list
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
### Pass User LLM API Keys, Fallbacks
|
||||
Allow your end-users to pass their model list, api base, OpenAI API key (any LiteLLM supported provider) to make requests
|
||||
|
||||
**Note** This is not related to [virtual keys](./virtual_keys.md). This is for when you want to pass in your users actual LLM API keys.
|
||||
|
||||
:::info
|
||||
|
||||
**You can pass a litellm.RouterConfig as `user_config`, See all supported params here https://github.com/BerriAI/litellm/blob/main/litellm/types/router.py **
|
||||
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="openai-py" label="OpenAI Python">
|
||||
|
||||
#### Step 1: Define user model list & config
|
||||
```python
|
||||
import os
|
||||
|
||||
user_config = {
|
||||
'model_list': [
|
||||
{
|
||||
'model_name': 'user-azure-instance',
|
||||
'litellm_params': {
|
||||
'model': 'azure/chatgpt-v-2',
|
||||
'api_key': os.getenv('AZURE_API_KEY'),
|
||||
'api_version': os.getenv('AZURE_API_VERSION'),
|
||||
'api_base': os.getenv('AZURE_API_BASE'),
|
||||
'timeout': 10,
|
||||
},
|
||||
'tpm': 240000,
|
||||
'rpm': 1800,
|
||||
},
|
||||
{
|
||||
'model_name': 'user-openai-instance',
|
||||
'litellm_params': {
|
||||
'model': 'gpt-3.5-turbo',
|
||||
'api_key': os.getenv('OPENAI_API_KEY'),
|
||||
'timeout': 10,
|
||||
},
|
||||
'tpm': 240000,
|
||||
'rpm': 1800,
|
||||
},
|
||||
],
|
||||
'num_retries': 2,
|
||||
'allowed_fails': 3,
|
||||
'fallbacks': [
|
||||
{
|
||||
'user-azure-instance': ['user-openai-instance']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### Step 2: Send user_config in `extra_body`
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# send request to `user-azure-instance`
|
||||
response = client.chat.completions.create(model="user-azure-instance", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={
|
||||
"user_config": user_config
|
||||
}
|
||||
) # 👈 User config
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="openai-js" label="OpenAI JS">
|
||||
|
||||
#### Step 1: Define user model list & config
|
||||
```javascript
|
||||
const os = require('os');
|
||||
|
||||
const userConfig = {
|
||||
model_list: [
|
||||
{
|
||||
model_name: 'user-azure-instance',
|
||||
litellm_params: {
|
||||
model: 'azure/chatgpt-v-2',
|
||||
api_key: process.env.AZURE_API_KEY,
|
||||
api_version: process.env.AZURE_API_VERSION,
|
||||
api_base: process.env.AZURE_API_BASE,
|
||||
timeout: 10,
|
||||
},
|
||||
tpm: 240000,
|
||||
rpm: 1800,
|
||||
},
|
||||
{
|
||||
model_name: 'user-openai-instance',
|
||||
litellm_params: {
|
||||
model: 'gpt-3.5-turbo',
|
||||
api_key: process.env.OPENAI_API_KEY,
|
||||
timeout: 10,
|
||||
},
|
||||
tpm: 240000,
|
||||
rpm: 1800,
|
||||
},
|
||||
],
|
||||
num_retries: 2,
|
||||
allowed_fails: 3,
|
||||
fallbacks: [
|
||||
{
|
||||
'user-azure-instance': ['user-openai-instance']
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
#### Step 2: Send `user_config` as a param to `openai.chat.completions.create`
|
||||
|
||||
```javascript
|
||||
const { OpenAI } = require('openai');
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: "sk-1234",
|
||||
baseURL: "http://0.0.0.0:4000"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: 'user', content: 'Say this is a test' }],
|
||||
model: 'gpt-3.5-turbo',
|
||||
user_config: userConfig // # 👈 User config
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### Pass User LLM API Keys / API Base
|
||||
Allows your users to pass in their OpenAI API key/API base (any LiteLLM supported provider) to make requests
|
||||
|
||||
Here's how to do it:
|
||||
|
||||
#### 1. Enable configurable clientside auth credentials for a provider
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "fireworks_ai/*"
|
||||
litellm_params:
|
||||
model: "fireworks_ai/*"
|
||||
configurable_clientside_auth_params: ["api_base"]
|
||||
# OR
|
||||
configurable_clientside_auth_params: [{"api_base": "^https://litellm.*direct\.fireworks\.ai/v1$"}] # 👈 regex
|
||||
```
|
||||
|
||||
Specify any/all auth params you want the user to be able to configure:
|
||||
|
||||
- api_base (✅ regex supported)
|
||||
- api_key
|
||||
- base_url
|
||||
|
||||
(check [provider docs](../providers/) for provider-specific auth params - e.g. `vertex_project`)
|
||||
|
||||
|
||||
#### 2. Test it!
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={"api_key": "my-bad-key", "api_base": "https://litellm-dev.direct.fireworks.ai/v1"}) # 👈 clientside credentials
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
More examples:
|
||||
<Tabs>
|
||||
<TabItem value="openai-py" label="Azure Credentials">
|
||||
|
||||
Pass in the litellm_params (E.g. api_key, api_base, etc.) via the `extra_body` parameter in the OpenAI client.
|
||||
|
||||
```python
|
||||
import openai
|
||||
client = openai.OpenAI(
|
||||
api_key="sk-1234",
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# request sent to model set on litellm proxy, `litellm --model`
|
||||
response = client.chat.completions.create(model="gpt-3.5-turbo", messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
],
|
||||
extra_body={
|
||||
"api_key": "my-azure-key",
|
||||
"api_base": "my-azure-base",
|
||||
"api_version": "my-azure-version"
|
||||
}) # 👈 User Key
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="openai-js" label="OpenAI JS">
|
||||
|
||||
For JS, the OpenAI client accepts passing params in the `create(..)` body as normal.
|
||||
|
||||
```javascript
|
||||
const { OpenAI } = require('openai');
|
||||
|
||||
const openai = new OpenAI({
|
||||
apiKey: "sk-1234",
|
||||
baseURL: "http://0.0.0.0:4000"
|
||||
});
|
||||
|
||||
async function main() {
|
||||
const chatCompletion = await openai.chat.completions.create({
|
||||
messages: [{ role: 'user', content: 'Say this is a test' }],
|
||||
model: 'gpt-3.5-turbo',
|
||||
api_key: "my-bad-key" // 👈 User Key
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
|
@ -224,272 +224,13 @@ Expected Response
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## **Model Access**
|
||||
|
||||
### **Restrict models by Virtual Key**
|
||||
|
||||
Set allowed models for a key using the `models` param
|
||||
|
||||
|
||||
```shell
|
||||
curl 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"models": ["gpt-3.5-turbo", "gpt-4"]}'
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
This key can only make requests to `models` that are `gpt-3.5-turbo` or `gpt-4`
|
||||
|
||||
:::
|
||||
|
||||
Verify this is set correctly by
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `models` for the key generated
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
### **Restrict models by `team_id`**
|
||||
`litellm-dev` can only access `azure-gpt-3.5`
|
||||
|
||||
**1. Create a team via `/team/new`**
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/team/new' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"team_alias": "litellm-dev",
|
||||
"models": ["azure-gpt-3.5"]
|
||||
}'
|
||||
|
||||
# returns {...,"team_id": "my-unique-id"}
|
||||
```
|
||||
|
||||
**2. Create a key for team**
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{"team_id": "my-unique-id"}'
|
||||
```
|
||||
|
||||
**3. Test it**
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer sk-qo992IjKOC2CHKZGRoJIGA' \
|
||||
--data '{
|
||||
"model": "BEDROCK_GROUP",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "hi"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
```shell
|
||||
{"error":{"message":"Invalid model for team litellm-dev: BEDROCK_GROUP. Valid models for team are: ['azure-gpt-3.5']\n\n\nTraceback (most recent call last):\n File \"/Users/ishaanjaffer/Github/litellm/litellm/proxy/proxy_server.py\", line 2298, in chat_completion\n _is_valid_team_configs(\n File \"/Users/ishaanjaffer/Github/litellm/litellm/proxy/utils.py\", line 1296, in _is_valid_team_configs\n raise Exception(\nException: Invalid model for team litellm-dev: BEDROCK_GROUP. Valid models for team are: ['azure-gpt-3.5']\n\n","type":"None","param":"None","code":500}}%
|
||||
```
|
||||
|
||||
### **Grant Access to new model (Access Groups)**
|
||||
|
||||
Use model access groups to give users access to select models, and add new ones to it over time (e.g. mistral, llama-2, etc.)
|
||||
|
||||
**Step 1. Assign model, access group in config.yaml**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/fake
|
||||
api_key: fake-key
|
||||
api_base: https://exampleopenaiendpoint-production.up.railway.app/
|
||||
model_info:
|
||||
access_groups: ["beta-models"] # 👈 Model Access Group
|
||||
- model_name: fireworks-llama-v3-70b-instruct
|
||||
litellm_params:
|
||||
model: fireworks_ai/accounts/fireworks/models/llama-v3-70b-instruct
|
||||
api_key: "os.environ/FIREWORKS"
|
||||
model_info:
|
||||
access_groups: ["beta-models"] # 👈 Model Access Group
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
|
||||
<TabItem value="key" label="Key Access Groups">
|
||||
|
||||
**Create key with access group**
|
||||
|
||||
```bash
|
||||
curl --location 'http://localhost:4000/key/generate' \
|
||||
-H 'Authorization: Bearer <your-master-key>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"models": ["beta-models"], # 👈 Model Access Group
|
||||
"max_budget": 0,}'
|
||||
```
|
||||
|
||||
Test Key
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `beta-models` access group
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="team" label="Team Access Groups">
|
||||
|
||||
Create Team
|
||||
|
||||
```shell
|
||||
curl --location 'http://localhost:4000/team/new' \
|
||||
-H 'Authorization: Bearer sk-<key-from-previous-step>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"models": ["beta-models"]}'
|
||||
```
|
||||
|
||||
Create Key for Team
|
||||
|
||||
```shell
|
||||
curl --location 'http://0.0.0.0:4000/key/generate' \
|
||||
--header 'Authorization: Bearer sk-<key-from-previous-step>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{"team_id": "0ac97648-c194-4c90-8cd6-40af7b0d2d2a"}
|
||||
```
|
||||
|
||||
|
||||
Test Key
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Allowed Access" value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Disallowed Access" value = "not-allowed">
|
||||
|
||||
:::info
|
||||
|
||||
Expect this to fail since gpt-4o is not in the `beta-models` access group
|
||||
|
||||
:::
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-<key-from-previous-step>" \
|
||||
-d '{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
### Model Aliases
|
||||
## Model Aliases
|
||||
|
||||
If a user is expected to use a given model (i.e. gpt3-5), and you want to:
|
||||
|
||||
- try to upgrade the request (i.e. GPT4)
|
||||
- or downgrade it (i.e. Mistral)
|
||||
- OR rotate the API KEY (i.e. open AI)
|
||||
- OR access the same model through different end points (i.e. openAI vs openrouter vs Azure)
|
||||
|
||||
Here's how you can do that:
|
||||
|
||||
|
|
@ -509,13 +250,13 @@ model_list:
|
|||
litellm_params:
|
||||
model: huggingface/HuggingFaceH4/zephyr-7b-beta
|
||||
api_base: http://0.0.0.0:8003
|
||||
- model_name: my-paid-tier
|
||||
- model_name: my-paid-tier
|
||||
litellm_params:
|
||||
model: gpt-4
|
||||
api_key: my-api-key
|
||||
```
|
||||
|
||||
**Step 2: Generate a user key - enabling them access to specific models, custom model aliases, etc.**
|
||||
**Step 2: Generate a key**
|
||||
|
||||
```bash
|
||||
curl -X POST "https://0.0.0.0:4000/key/generate" \
|
||||
|
|
@ -523,13 +264,29 @@ curl -X POST "https://0.0.0.0:4000/key/generate" \
|
|||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"models": ["my-free-tier"],
|
||||
"aliases": {"gpt-3.5-turbo": "my-free-tier"},
|
||||
"aliases": {"gpt-3.5-turbo": "my-free-tier"}, # 👈 KEY CHANGE
|
||||
"duration": "30min"
|
||||
}'
|
||||
```
|
||||
|
||||
- **How to upgrade / downgrade request?** Change the alias mapping
|
||||
- **How are routing between diff keys/api bases done?** litellm handles this by shuffling between different models in the model list with the same model_name. [**See Code**](https://github.com/BerriAI/litellm/blob/main/litellm/router.py)
|
||||
|
||||
**Step 3: Test the key**
|
||||
|
||||
```bash
|
||||
curl -X POST "https://0.0.0.0:4000/key/generate" \
|
||||
-H "Authorization: Bearer <user-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "this is a test request, write a short poem"
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
## Advanced
|
||||
|
|
|
|||
|
|
@ -138,3 +138,6 @@ curl http://localhost:4000/v1/chat/completions \
|
|||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## [[PROXY-Only] Control Wildcard Model Access](./proxy/model_access#-control-access-on-wildcard-models)
|
||||
|
|
@ -43,6 +43,12 @@ const config = {
|
|||
id: 'release_notes',
|
||||
path: './release_notes', // Folder where your release notes are stored
|
||||
routeBasePath: '/release_notes', // URL path for the release notes
|
||||
sortPosts: (a, b) => {
|
||||
// Extract folder names from the file paths
|
||||
const folderA = a.metadata.permalink.split('/')[2]; // Get folder name from permalink
|
||||
const folderB = b.metadata.permalink.split('/')[2];
|
||||
return folderA.localeCompare(folderB); // Compare folder names
|
||||
},
|
||||
include: ['**/*.md', '**/*.mdx'], // Files to include
|
||||
// Other blog options
|
||||
},
|
||||
|
|
|
|||
BIN
docs/my-website/img/gd_fail.png
Normal file
BIN
docs/my-website/img/gd_fail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 380 KiB |
BIN
docs/my-website/img/gd_success.png
Normal file
BIN
docs/my-website/img/gd_success.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
BIN
docs/my-website/img/release_notes/ui_format.png
Normal file
BIN
docs/my-website/img/release_notes/ui_format.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 275 KiB |
BIN
docs/my-website/img/release_notes/ui_model.png
Normal file
BIN
docs/my-website/img/release_notes/ui_model.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
BIN
docs/my-website/img/ui_key.png
Normal file
BIN
docs/my-website/img/ui_key.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
100
docs/my-website/release_notes/v1.56.3/index.md
Normal file
100
docs/my-website/release_notes/v1.56.3/index.md
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
|
||||
`guardrails`, `logging`, `virtual key management`, `new models`
|
||||
|
||||
:::info
|
||||
|
||||
Get a 7 day free trial for LiteLLM Enterprise [here](https://litellm.ai/#trial).
|
||||
|
||||
**no call needed**
|
||||
|
||||
:::
|
||||
|
||||
## New Features
|
||||
|
||||
### ✨ Log Guardrail Traces
|
||||
|
||||
Track guardrail failure rate and if a guardrail is going rogue and failing requests. [Start here](https://docs.litellm.ai/docs/proxy/guardrails/quick_start)
|
||||
|
||||
|
||||
#### Traced Guardrail Success
|
||||
|
||||
<Image img={require('../../img/gd_success.png')} />
|
||||
|
||||
#### Traced Guardrail Failure
|
||||
|
||||
<Image img={require('../../img/gd_fail.png')} />
|
||||
|
||||
|
||||
### `/guardrails/list`
|
||||
|
||||
`/guardrails/list` allows clients to view available guardrails + supported guardrail params
|
||||
|
||||
|
||||
```shell
|
||||
curl -X GET 'http://0.0.0.0:4000/guardrails/list'
|
||||
```
|
||||
|
||||
Expected response
|
||||
|
||||
```json
|
||||
{
|
||||
"guardrails": [
|
||||
{
|
||||
"guardrail_name": "aporia-post-guard",
|
||||
"guardrail_info": {
|
||||
"params": [
|
||||
{
|
||||
"name": "toxicity_score",
|
||||
"type": "float",
|
||||
"description": "Score between 0-1 indicating content toxicity level"
|
||||
},
|
||||
{
|
||||
"name": "pii_detection",
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### ✨ Guardrails with Mock LLM
|
||||
|
||||
|
||||
Send `mock_response` to test guardrails without making an LLM call. More info on `mock_response` [here](https://docs.litellm.ai/docs/proxy/guardrails/quick_start)
|
||||
|
||||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-npnwjPQciVRok5yNZgKmFQ" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
{"role": "user", "content": "hi my email is ishaan@berri.ai"}
|
||||
],
|
||||
"mock_response": "This is a mock response",
|
||||
"guardrails": ["aporia-pre-guard", "aporia-post-guard"]
|
||||
}'
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Assign Keys to Users
|
||||
|
||||
You can now assign keys to users via Proxy UI
|
||||
|
||||
|
||||
<Image img={require('../../img/ui_key.png')} />
|
||||
|
||||
## New Models
|
||||
|
||||
- `openrouter/openai/o1`
|
||||
- `vertex_ai/mistral-large@2411`
|
||||
|
||||
## Fixes
|
||||
|
||||
- Fix `vertex_ai/` mistral model pricing: https://github.com/BerriAI/litellm/pull/7345
|
||||
- Missing model_group field in logs for aspeech call types https://github.com/BerriAI/litellm/pull/7392
|
||||
55
docs/my-website/release_notes/v1.56.4/index.md
Normal file
55
docs/my-website/release_notes/v1.56.4/index.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
|
||||
|
||||
`deepgram`, `fireworks ai`, `vision`, `admin ui`, `dependency upgrades`
|
||||
|
||||
## New Models
|
||||
|
||||
### **Deepgram Speech to Text**
|
||||
|
||||
New Speech to Text support for Deepgram models. [**Start Here**](https://docs.litellm.ai/docs/providers/deepgram)
|
||||
|
||||
```python
|
||||
from litellm import transcription
|
||||
import os
|
||||
|
||||
# set api keys
|
||||
os.environ["DEEPGRAM_API_KEY"] = ""
|
||||
audio_file = open("/path/to/audio.mp3", "rb")
|
||||
|
||||
response = transcription(model="deepgram/nova-2", file=audio_file)
|
||||
|
||||
print(f"response: {response}")
|
||||
```
|
||||
|
||||
### **Fireworks AI - Vision** support for all models
|
||||
LiteLLM supports document inlining for Fireworks AI models. This is useful for models that are not vision models, but still need to parse documents/images/etc.
|
||||
LiteLLM will add `#transform=inline` to the url of the image_url, if the model is not a vision model [See Code](https://github.com/BerriAI/litellm/blob/1ae9d45798bdaf8450f2dfdec703369f3d2212b7/litellm/llms/fireworks_ai/chat/transformation.py#L114)
|
||||
|
||||
|
||||
## Proxy Admin UI
|
||||
|
||||
- `Test Key` Tab displays `model` used in response
|
||||
|
||||
<Image img={require('../../img/release_notes/ui_model.png')} />
|
||||
|
||||
- `Test Key` Tab renders content in `.md`, `.py` (any code/markdown format)
|
||||
|
||||
<Image img={require('../../img/release_notes/ui_format.png')} />
|
||||
|
||||
|
||||
## Dependency Upgrades
|
||||
|
||||
- (Security fix) Upgrade to `fastapi==0.115.5` https://github.com/BerriAI/litellm/pull/7447
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- Add health check support for realtime models [Here](https://docs.litellm.ai/docs/proxy/health#realtime-models)
|
||||
- Health check error with audio_transcription model https://github.com/BerriAI/litellm/issues/5999
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -45,6 +45,7 @@ const sidebars = {
|
|||
"proxy/health",
|
||||
"proxy/debugging",
|
||||
"proxy/pass_through",
|
||||
"proxy/spending_monitoring",
|
||||
],
|
||||
},
|
||||
"proxy/demo",
|
||||
|
|
@ -64,6 +65,7 @@ const sidebars = {
|
|||
label: "Making LLM Requests",
|
||||
items: [
|
||||
"proxy/user_keys",
|
||||
"proxy/clientside_auth",
|
||||
"proxy/response_headers",
|
||||
],
|
||||
},
|
||||
|
|
@ -80,6 +82,14 @@ const sidebars = {
|
|||
"proxy/multiple_admins",
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Model Access",
|
||||
items: [
|
||||
"proxy/model_access",
|
||||
"proxy/team_model_add"
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Admin UI",
|
||||
|
|
@ -90,13 +100,6 @@ const sidebars = {
|
|||
"proxy/custom_sso"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Team Management",
|
||||
items: [
|
||||
"proxy/team_model_add"
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
label: "Spend Tracking",
|
||||
|
|
@ -181,6 +184,7 @@ const sidebars = {
|
|||
"providers/anyscale",
|
||||
"providers/huggingface",
|
||||
"providers/databricks",
|
||||
"providers/deepgram",
|
||||
"providers/watsonx",
|
||||
"providers/predibase",
|
||||
"providers/nvidia_nim",
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ use_client: bool = False
|
|||
ssl_verify: Union[str, bool] = True
|
||||
ssl_certificate: Optional[str] = None
|
||||
disable_streaming_logging: bool = False
|
||||
disable_add_transform_inline_image_block: bool = False
|
||||
in_memory_llm_clients_cache: InMemoryCache = InMemoryCache()
|
||||
safe_memory_mode: bool = False
|
||||
enable_azure_ad_token_refresh: Optional[bool] = False
|
||||
|
|
@ -1107,6 +1108,9 @@ from .llms.cohere.chat.transformation import CohereChatConfig
|
|||
from .llms.bedrock.embed.cohere_transformation import BedrockCohereEmbeddingConfig
|
||||
from .llms.openai.openai import OpenAIConfig, MistralEmbeddingConfig
|
||||
from .llms.deepinfra.chat.transformation import DeepInfraConfig
|
||||
from .llms.deepgram.audio_transcription.transformation import (
|
||||
DeepgramAudioTranscriptionConfig,
|
||||
)
|
||||
from litellm.llms.openai.completion.transformation import OpenAITextCompletionConfig
|
||||
from .llms.groq.chat.transformation import GroqChatConfig
|
||||
from .llms.voyage.embedding.transformation import VoyageEmbeddingConfig
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
# # set AUTH STRATEGY FOR LLM APIs - Defaults to using Environment Variables
|
||||
# AUTH_STRATEGY = "ENV" # ENV or DYNAMIC, ENV always reads from environment variables, DYNAMIC reads request headers to set LLM api keys
|
||||
|
||||
# OPENAI_API_KEY = ""
|
||||
|
||||
# HUGGINGFACE_API_KEY=""
|
||||
|
||||
# TOGETHERAI_API_KEY=""
|
||||
|
||||
# REPLICATE_API_KEY=""
|
||||
|
||||
# ## bedrock / sagemaker
|
||||
# AWS_ACCESS_KEY_ID = ""
|
||||
# AWS_SECRET_ACCESS_KEY = ""
|
||||
|
||||
# AZURE_API_KEY = ""
|
||||
# AZURE_API_BASE = ""
|
||||
# AZURE_API_VERSION = ""
|
||||
|
||||
# ANTHROPIC_API_KEY = ""
|
||||
|
||||
# COHERE_API_KEY = ""
|
||||
|
||||
# ## CONFIG FILE ##
|
||||
# # CONFIG_FILE_PATH = "" # uncomment to point to config file
|
||||
|
||||
# ## LOGGING ##
|
||||
|
||||
# SET_VERBOSE = "False" # set to 'True' to see detailed input/output logs
|
||||
|
||||
# ### LANGFUSE
|
||||
# LANGFUSE_PUBLIC_KEY = ""
|
||||
# LANGFUSE_SECRET_KEY = ""
|
||||
# # Optional, defaults to https://cloud.langfuse.com
|
||||
# LANGFUSE_HOST = "" # optional
|
||||
|
||||
|
||||
# ## CACHING ##
|
||||
|
||||
# ### REDIS
|
||||
# REDIS_HOST = ""
|
||||
# REDIS_PORT = ""
|
||||
# REDIS_PASSWORD = ""
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# FROM python:3.10
|
||||
|
||||
# ENV LITELLM_CONFIG_PATH="/litellm.secrets.toml"
|
||||
# COPY . /app
|
||||
# WORKDIR /app
|
||||
# RUN pip install -r requirements.txt
|
||||
|
||||
# EXPOSE $PORT
|
||||
|
||||
# CMD exec uvicorn main:app --host 0.0.0.0 --port $PORT --workers 10
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# litellm-server [experimental]
|
||||
|
||||
Deprecated. See litellm/proxy
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# from .main import *
|
||||
# from .server_utils import *
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
# import os, traceback
|
||||
# from fastapi import FastAPI, Request, HTTPException
|
||||
# from fastapi.routing import APIRouter
|
||||
# from fastapi.responses import StreamingResponse, FileResponse
|
||||
# from fastapi.middleware.cors import CORSMiddleware
|
||||
# import json, sys
|
||||
# from typing import Optional
|
||||
# sys.path.insert(
|
||||
# 0, os.path.abspath("../")
|
||||
# ) # Adds the parent directory to the system path - for litellm local dev
|
||||
# import litellm
|
||||
|
||||
# try:
|
||||
# from litellm.deprecated_litellm_server.server_utils import set_callbacks, load_router_config, print_verbose
|
||||
# except ImportError:
|
||||
# from litellm.deprecated_litellm_server.server_utils import set_callbacks, load_router_config, print_verbose
|
||||
# import dotenv
|
||||
# dotenv.load_dotenv() # load env variables
|
||||
|
||||
# app = FastAPI(docs_url="/", title="LiteLLM API")
|
||||
# router = APIRouter()
|
||||
# origins = ["*"]
|
||||
|
||||
# app.add_middleware(
|
||||
# CORSMiddleware,
|
||||
# allow_origins=origins,
|
||||
# allow_credentials=True,
|
||||
# allow_methods=["*"],
|
||||
# allow_headers=["*"],
|
||||
# )
|
||||
# #### GLOBAL VARIABLES ####
|
||||
# llm_router: Optional[litellm.Router] = None
|
||||
# llm_model_list: Optional[list] = None
|
||||
# server_settings: Optional[dict] = None
|
||||
|
||||
# set_callbacks() # sets litellm callbacks for logging if they exist in the environment
|
||||
|
||||
# if "CONFIG_FILE_PATH" in os.environ:
|
||||
# llm_router, llm_model_list, server_settings = load_router_config(router=llm_router, config_file_path=os.getenv("CONFIG_FILE_PATH"))
|
||||
# else:
|
||||
# llm_router, llm_model_list, server_settings = load_router_config(router=llm_router)
|
||||
# #### API ENDPOINTS ####
|
||||
# @router.get("/v1/models")
|
||||
# @router.get("/models") # if project requires model list
|
||||
# def model_list():
|
||||
# all_models = litellm.utils.get_valid_models()
|
||||
# if llm_model_list:
|
||||
# all_models += llm_model_list
|
||||
# return dict(
|
||||
# data=[
|
||||
# {
|
||||
# "id": model,
|
||||
# "object": "model",
|
||||
# "created": 1677610602,
|
||||
# "owned_by": "openai",
|
||||
# }
|
||||
# for model in all_models
|
||||
# ],
|
||||
# object="list",
|
||||
# )
|
||||
# # for streaming
|
||||
# def data_generator(response):
|
||||
|
||||
# for chunk in response:
|
||||
|
||||
# yield f"data: {json.dumps(chunk)}\n\n"
|
||||
|
||||
# @router.post("/v1/completions")
|
||||
# @router.post("/completions")
|
||||
# async def completion(request: Request):
|
||||
# data = await request.json()
|
||||
# response = litellm.completion(
|
||||
# **data
|
||||
# )
|
||||
# if 'stream' in data and data['stream'] == True: # use generate_responses to stream responses
|
||||
# return StreamingResponse(data_generator(response), media_type='text/event-stream')
|
||||
# return response
|
||||
|
||||
# @router.post("/v1/embeddings")
|
||||
# @router.post("/embeddings")
|
||||
# async def embedding(request: Request):
|
||||
# try:
|
||||
# data = await request.json()
|
||||
# # default to always using the "ENV" variables, only if AUTH_STRATEGY==DYNAMIC then reads headers
|
||||
# if os.getenv("AUTH_STRATEGY", None) == "DYNAMIC" and "authorization" in request.headers: # if users pass LLM api keys as part of header
|
||||
# api_key = request.headers.get("authorization")
|
||||
# api_key = api_key.replace("Bearer", "").strip() # type: ignore
|
||||
# if len(api_key.strip()) > 0:
|
||||
# api_key = api_key
|
||||
# data["api_key"] = api_key
|
||||
# response = litellm.embedding(
|
||||
# **data
|
||||
# )
|
||||
# return response
|
||||
# except Exception as e:
|
||||
# error_traceback = traceback.format_exc()
|
||||
# error_msg = f"{str(e)}\n\n{error_traceback}"
|
||||
# return {"error": error_msg}
|
||||
|
||||
# @router.post("/v1/chat/completions")
|
||||
# @router.post("/chat/completions")
|
||||
# @router.post("/openai/deployments/{model:path}/chat/completions") # azure compatible endpoint
|
||||
# async def chat_completion(request: Request, model: Optional[str] = None):
|
||||
# global llm_model_list, server_settings
|
||||
# try:
|
||||
# data = await request.json()
|
||||
# server_model = server_settings.get("completion_model", None) if server_settings else None
|
||||
# data["model"] = server_model or model or data["model"]
|
||||
# ## CHECK KEYS ##
|
||||
# # default to always using the "ENV" variables, only if AUTH_STRATEGY==DYNAMIC then reads headers
|
||||
# # env_validation = litellm.validate_environment(model=data["model"])
|
||||
# # if (env_validation['keys_in_environment'] is False or os.getenv("AUTH_STRATEGY", None) == "DYNAMIC") and ("authorization" in request.headers or "api-key" in request.headers): # if users pass LLM api keys as part of header
|
||||
# # if "authorization" in request.headers:
|
||||
# # api_key = request.headers.get("authorization")
|
||||
# # elif "api-key" in request.headers:
|
||||
# # api_key = request.headers.get("api-key")
|
||||
# # print(f"api_key in headers: {api_key}")
|
||||
# # if " " in api_key:
|
||||
# # api_key = api_key.split(" ")[1]
|
||||
# # print(f"api_key split: {api_key}")
|
||||
# # if len(api_key) > 0:
|
||||
# # api_key = api_key
|
||||
# # data["api_key"] = api_key
|
||||
# # print(f"api_key in data: {api_key}")
|
||||
# ## CHECK CONFIG ##
|
||||
# if llm_model_list and data["model"] in [m["model_name"] for m in llm_model_list]:
|
||||
# for m in llm_model_list:
|
||||
# if data["model"] == m["model_name"]:
|
||||
# for key, value in m["litellm_params"].items():
|
||||
# data[key] = value
|
||||
# break
|
||||
# response = litellm.completion(
|
||||
# **data
|
||||
# )
|
||||
# if 'stream' in data and data['stream'] == True: # use generate_responses to stream responses
|
||||
# return StreamingResponse(data_generator(response), media_type='text/event-stream')
|
||||
# return response
|
||||
# except Exception as e:
|
||||
# error_traceback = traceback.format_exc()
|
||||
|
||||
# error_msg = f"{str(e)}\n\n{error_traceback}"
|
||||
# # return {"error": error_msg}
|
||||
# raise HTTPException(status_code=500, detail=error_msg)
|
||||
|
||||
# @router.post("/router/completions")
|
||||
# async def router_completion(request: Request):
|
||||
# global llm_router
|
||||
# try:
|
||||
# data = await request.json()
|
||||
# if "model_list" in data:
|
||||
# llm_router = litellm.Router(model_list=data.pop("model_list"))
|
||||
# if llm_router is None:
|
||||
# raise Exception("Save model list via config.yaml. Eg.: ` docker build -t myapp --build-arg CONFIG_FILE=myconfig.yaml .` or pass it in as model_list=[..] as part of the request body")
|
||||
|
||||
# # openai.ChatCompletion.create replacement
|
||||
# response = await llm_router.acompletion(model="gpt-3.5-turbo",
|
||||
# messages=[{"role": "user", "content": "Hey, how's it going?"}])
|
||||
|
||||
# if 'stream' in data and data['stream'] == True: # use generate_responses to stream responses
|
||||
# return StreamingResponse(data_generator(response), media_type='text/event-stream')
|
||||
# return response
|
||||
# except Exception as e:
|
||||
# error_traceback = traceback.format_exc()
|
||||
# error_msg = f"{str(e)}\n\n{error_traceback}"
|
||||
# return {"error": error_msg}
|
||||
|
||||
# @router.post("/router/embedding")
|
||||
# async def router_embedding(request: Request):
|
||||
# global llm_router
|
||||
# try:
|
||||
# data = await request.json()
|
||||
# if "model_list" in data:
|
||||
# llm_router = litellm.Router(model_list=data.pop("model_list"))
|
||||
# if llm_router is None:
|
||||
# raise Exception("Save model list via config.yaml. Eg.: ` docker build -t myapp --build-arg CONFIG_FILE=myconfig.yaml .` or pass it in as model_list=[..] as part of the request body")
|
||||
|
||||
# response = await llm_router.aembedding(model="gpt-3.5-turbo", # type: ignore
|
||||
# messages=[{"role": "user", "content": "Hey, how's it going?"}])
|
||||
|
||||
# if 'stream' in data and data['stream'] == True: # use generate_responses to stream responses
|
||||
# return StreamingResponse(data_generator(response), media_type='text/event-stream')
|
||||
# return response
|
||||
# except Exception as e:
|
||||
# error_traceback = traceback.format_exc()
|
||||
# error_msg = f"{str(e)}\n\n{error_traceback}"
|
||||
# return {"error": error_msg}
|
||||
|
||||
# @router.get("/")
|
||||
# async def home(request: Request):
|
||||
# return "LiteLLM: RUNNING"
|
||||
|
||||
|
||||
# app.include_router(router)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# openai
|
||||
# fastapi
|
||||
# uvicorn
|
||||
# boto3
|
||||
# litellm
|
||||
# python-dotenv
|
||||
# redis
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
# import os, litellm
|
||||
# import pkg_resources
|
||||
# import dotenv
|
||||
# dotenv.load_dotenv() # load env variables
|
||||
|
||||
# def print_verbose(print_statement):
|
||||
# pass
|
||||
|
||||
# def get_package_version(package_name):
|
||||
# try:
|
||||
# package = pkg_resources.get_distribution(package_name)
|
||||
# return package.version
|
||||
# except pkg_resources.DistributionNotFound:
|
||||
# return None
|
||||
|
||||
# # Usage example
|
||||
# package_name = "litellm"
|
||||
# version = get_package_version(package_name)
|
||||
# if version:
|
||||
# print_verbose(f"The version of {package_name} is {version}")
|
||||
# else:
|
||||
# print_verbose(f"{package_name} is not installed")
|
||||
# import yaml
|
||||
# import dotenv
|
||||
# from typing import Optional
|
||||
# dotenv.load_dotenv() # load env variables
|
||||
|
||||
# def set_callbacks():
|
||||
# ## LOGGING
|
||||
# if len(os.getenv("SET_VERBOSE", "")) > 0:
|
||||
# if os.getenv("SET_VERBOSE") == "True":
|
||||
# litellm.set_verbose = True
|
||||
# print_verbose("\033[92mLiteLLM: Switched on verbose logging\033[0m")
|
||||
# else:
|
||||
# litellm.set_verbose = False
|
||||
|
||||
# ### LANGFUSE
|
||||
# if (len(os.getenv("LANGFUSE_PUBLIC_KEY", "")) > 0 and len(os.getenv("LANGFUSE_SECRET_KEY", ""))) > 0 or len(os.getenv("LANGFUSE_HOST", "")) > 0:
|
||||
# litellm.success_callback = ["langfuse"]
|
||||
# print_verbose("\033[92mLiteLLM: Switched on Langfuse feature\033[0m")
|
||||
|
||||
# ## CACHING
|
||||
# ### REDIS
|
||||
# # if len(os.getenv("REDIS_HOST", "")) > 0 and len(os.getenv("REDIS_PORT", "")) > 0 and len(os.getenv("REDIS_PASSWORD", "")) > 0:
|
||||
# # print(f"redis host: {os.getenv('REDIS_HOST')}; redis port: {os.getenv('REDIS_PORT')}; password: {os.getenv('REDIS_PASSWORD')}")
|
||||
# # from litellm.caching.caching import Cache
|
||||
# # litellm.cache = Cache(type="redis", host=os.getenv("REDIS_HOST"), port=os.getenv("REDIS_PORT"), password=os.getenv("REDIS_PASSWORD"))
|
||||
# # print("\033[92mLiteLLM: Switched on Redis caching\033[0m")
|
||||
|
||||
|
||||
# def load_router_config(router: Optional[litellm.Router], config_file_path: Optional[str]='/app/config.yaml'):
|
||||
# config = {}
|
||||
# server_settings = {}
|
||||
# try:
|
||||
# if os.path.exists(config_file_path): # type: ignore
|
||||
# with open(config_file_path, 'r') as file: # type: ignore
|
||||
# config = yaml.safe_load(file)
|
||||
# else:
|
||||
# pass
|
||||
# except Exception:
|
||||
# pass
|
||||
|
||||
# ## SERVER SETTINGS (e.g. default completion model = 'ollama/mistral')
|
||||
# server_settings = config.get("server_settings", None)
|
||||
# if server_settings:
|
||||
# server_settings = server_settings
|
||||
|
||||
# ## LITELLM MODULE SETTINGS (e.g. litellm.drop_params=True,..)
|
||||
# litellm_settings = config.get('litellm_settings', None)
|
||||
# if litellm_settings:
|
||||
# for key, value in litellm_settings.items():
|
||||
# setattr(litellm, key, value)
|
||||
|
||||
# ## MODEL LIST
|
||||
# model_list = config.get('model_list', None)
|
||||
# if model_list:
|
||||
# router = litellm.Router(model_list=model_list)
|
||||
|
||||
# ## ENVIRONMENT VARIABLES
|
||||
# environment_variables = config.get('environment_variables', None)
|
||||
# if environment_variables:
|
||||
# for key, value in environment_variables.items():
|
||||
# os.environ[key] = value
|
||||
|
||||
# return router, model_list, server_settings
|
||||
|
|
@ -337,20 +337,22 @@ class ContextWindowExceededError(BadRequestError): # type: ignore
|
|||
litellm_debug_info: Optional[str] = None,
|
||||
):
|
||||
self.status_code = 400
|
||||
self.message = "litellm.ContextWindowExceededError: {}".format(message)
|
||||
self.model = model
|
||||
self.llm_provider = llm_provider
|
||||
self.litellm_debug_info = litellm_debug_info
|
||||
request = httpx.Request(method="POST", url="https://api.openai.com/v1")
|
||||
self.response = httpx.Response(status_code=400, request=request)
|
||||
super().__init__(
|
||||
message=self.message,
|
||||
message=message,
|
||||
model=self.model, # type: ignore
|
||||
llm_provider=self.llm_provider, # type: ignore
|
||||
response=self.response,
|
||||
litellm_debug_info=self.litellm_debug_info,
|
||||
) # Call the base class constructor with the parameters it needs
|
||||
|
||||
# set after, to make it clear the raised error is a context window exceeded error
|
||||
self.message = "litellm.ContextWindowExceededError: {}".format(self.message)
|
||||
|
||||
def __str__(self):
|
||||
_message = self.message
|
||||
if self.num_retries:
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ def create_fine_tuning_job(
|
|||
response = openai_fine_tuning_apis_instance.create_fine_tuning_job(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
api_version=optional_params.api_version,
|
||||
organization=organization,
|
||||
create_fine_tuning_job_data=create_fine_tuning_job_data_dict,
|
||||
timeout=timeout,
|
||||
|
|
@ -223,6 +224,7 @@ def create_fine_tuning_job(
|
|||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
_is_async=_is_async,
|
||||
organization=optional_params.organization,
|
||||
)
|
||||
elif custom_llm_provider == "vertex_ai":
|
||||
api_base = optional_params.api_base or ""
|
||||
|
|
@ -279,7 +281,7 @@ def create_fine_tuning_job(
|
|||
|
||||
async def acancel_fine_tuning_job(
|
||||
fine_tuning_job_id: str,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure", "vertex_ai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
|
|
@ -374,6 +376,7 @@ def cancel_fine_tuning_job(
|
|||
response = openai_fine_tuning_apis_instance.cancel_fine_tuning_job(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
api_version=optional_params.api_version,
|
||||
organization=organization,
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
timeout=timeout,
|
||||
|
|
@ -412,6 +415,7 @@ def cancel_fine_tuning_job(
|
|||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
_is_async=_is_async,
|
||||
organization=optional_params.organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
|
|
@ -434,7 +438,7 @@ def cancel_fine_tuning_job(
|
|||
async def alist_fine_tuning_jobs(
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure", "vertex_ai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
|
|
@ -533,6 +537,7 @@ def list_fine_tuning_jobs(
|
|||
response = openai_fine_tuning_apis_instance.list_fine_tuning_jobs(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
api_version=optional_params.api_version,
|
||||
organization=organization,
|
||||
after=after,
|
||||
limit=limit,
|
||||
|
|
@ -573,6 +578,7 @@ def list_fine_tuning_jobs(
|
|||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
_is_async=_is_async,
|
||||
organization=optional_params.organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
|
|
@ -590,3 +596,153 @@ def list_fine_tuning_jobs(
|
|||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
async def aretrieve_fine_tuning_job(
|
||||
fine_tuning_job_id: str,
|
||||
custom_llm_provider: Literal["openai", "azure", "vertex_ai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> FineTuningJob:
|
||||
"""
|
||||
Async: Get info about a fine-tuning job.
|
||||
"""
|
||||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["aretrieve_fine_tuning_job"] = True
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
retrieve_fine_tuning_job,
|
||||
fine_tuning_job_id,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
extra_body,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Add the context to the function
|
||||
ctx = contextvars.copy_context()
|
||||
func_with_context = partial(ctx.run, func)
|
||||
init_response = await loop.run_in_executor(None, func_with_context)
|
||||
if asyncio.iscoroutine(init_response):
|
||||
response = await init_response
|
||||
else:
|
||||
response = init_response # type: ignore
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
def retrieve_fine_tuning_job(
|
||||
fine_tuning_job_id: str,
|
||||
custom_llm_provider: Literal["openai", "azure", "vertex_ai"] = "openai",
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
**kwargs,
|
||||
) -> Union[FineTuningJob, Coroutine[Any, Any, FineTuningJob]]:
|
||||
"""
|
||||
Get info about a fine-tuning job.
|
||||
"""
|
||||
try:
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
### TIMEOUT LOGIC ###
|
||||
timeout = optional_params.timeout or kwargs.get("request_timeout", 600) or 600
|
||||
# set timeout for 10 minutes by default
|
||||
|
||||
if (
|
||||
timeout is not None
|
||||
and isinstance(timeout, httpx.Timeout)
|
||||
and supports_httpx_timeout(custom_llm_provider) is False
|
||||
):
|
||||
read_timeout = timeout.read or 600
|
||||
timeout = read_timeout # default 10 min timeout
|
||||
elif timeout is not None and not isinstance(timeout, httpx.Timeout):
|
||||
timeout = float(timeout) # type: ignore
|
||||
elif timeout is None:
|
||||
timeout = 600.0
|
||||
|
||||
_is_async = kwargs.pop("aretrieve_fine_tuning_job", False) is True
|
||||
|
||||
# OpenAI
|
||||
if custom_llm_provider == "openai":
|
||||
api_base = (
|
||||
optional_params.api_base
|
||||
or litellm.api_base
|
||||
or os.getenv("OPENAI_API_BASE")
|
||||
or "https://api.openai.com/v1"
|
||||
)
|
||||
organization = (
|
||||
optional_params.organization
|
||||
or litellm.organization
|
||||
or os.getenv("OPENAI_ORGANIZATION", None)
|
||||
or None
|
||||
)
|
||||
api_key = (
|
||||
optional_params.api_key
|
||||
or litellm.api_key
|
||||
or litellm.openai_key
|
||||
or os.getenv("OPENAI_API_KEY")
|
||||
)
|
||||
|
||||
response = openai_fine_tuning_apis_instance.retrieve_fine_tuning_job(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
api_version=optional_params.api_version,
|
||||
organization=organization,
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
_is_async=_is_async,
|
||||
)
|
||||
# Azure OpenAI
|
||||
elif custom_llm_provider == "azure":
|
||||
api_base = optional_params.api_base or litellm.api_base or get_secret_str("AZURE_API_BASE") # type: ignore
|
||||
|
||||
api_version = (
|
||||
optional_params.api_version
|
||||
or litellm.api_version
|
||||
or get_secret_str("AZURE_API_VERSION")
|
||||
) # type: ignore
|
||||
|
||||
api_key = (
|
||||
optional_params.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")
|
||||
) # type: ignore
|
||||
|
||||
extra_body = optional_params.get("extra_body", {})
|
||||
if extra_body is not None:
|
||||
extra_body.pop("azure_ad_token", None)
|
||||
else:
|
||||
get_secret_str("AZURE_AD_TOKEN") # type: ignore
|
||||
|
||||
response = azure_fine_tuning_apis_instance.retrieve_fine_tuning_job(
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
api_version=api_version,
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
timeout=timeout,
|
||||
max_retries=optional_params.max_retries,
|
||||
_is_async=_is_async,
|
||||
organization=optional_params.organization,
|
||||
)
|
||||
else:
|
||||
raise litellm.exceptions.BadRequestError(
|
||||
message="LiteLLM doesn't support {} for 'retrieve_fine_tuning_job'. Only 'openai' and 'azure' are supported.".format(
|
||||
custom_llm_provider
|
||||
),
|
||||
model="n/a",
|
||||
llm_provider=custom_llm_provider,
|
||||
response=httpx.Response(
|
||||
status_code=400,
|
||||
content="Unsupported provider",
|
||||
request=httpx.Request(method="retrieve_fine_tuning_job", url="https://github.com/BerriAI/litellm"), # type: ignore
|
||||
),
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from typing import Dict, List, Optional, Union
|
||||
from typing import Dict, List, Literal, Optional, Union
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.types.guardrails import DynamicGuardrailParams, GuardrailEventHooks
|
||||
from litellm.types.utils import StandardLoggingGuardrailInformation
|
||||
|
||||
|
||||
class CustomGuardrail(CustomLogger):
|
||||
|
|
@ -119,3 +120,101 @@ class CustomGuardrail(CustomLogger):
|
|||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def add_standard_logging_guardrail_information_to_request_data(
|
||||
self,
|
||||
guardrail_json_response: Union[Exception, str, dict],
|
||||
request_data: dict,
|
||||
guardrail_status: Literal["success", "failure"],
|
||||
) -> None:
|
||||
"""
|
||||
Builds `StandardLoggingGuardrailInformation` and adds it to the request metadata so it can be used for logging to DataDog, Langfuse, etc.
|
||||
"""
|
||||
from litellm.proxy.proxy_server import premium_user
|
||||
|
||||
if premium_user is not True:
|
||||
verbose_logger.warning(
|
||||
f"Guardrail Tracing is only available for premium users. Skipping guardrail logging for guardrail={self.guardrail_name} event_hook={self.event_hook}"
|
||||
)
|
||||
return
|
||||
if isinstance(guardrail_json_response, Exception):
|
||||
guardrail_json_response = str(guardrail_json_response)
|
||||
slg = StandardLoggingGuardrailInformation(
|
||||
guardrail_name=self.guardrail_name,
|
||||
guardrail_mode=self.event_hook,
|
||||
guardrail_response=guardrail_json_response,
|
||||
guardrail_status=guardrail_status,
|
||||
)
|
||||
if "metadata" in request_data:
|
||||
request_data["metadata"]["standard_logging_guardrail_information"] = slg
|
||||
elif "litellm_metadata" in request_data:
|
||||
request_data["litellm_metadata"][
|
||||
"standard_logging_guardrail_information"
|
||||
] = slg
|
||||
else:
|
||||
verbose_logger.warning(
|
||||
"unable to log guardrail information. No metadata found in request_data"
|
||||
)
|
||||
|
||||
|
||||
def log_guardrail_information(func):
|
||||
"""
|
||||
Decorator to add standard logging guardrail information to any function
|
||||
|
||||
Add this decorator to ensure your guardrail response is logged to DataDog, OTEL, s3, GCS etc.
|
||||
|
||||
Logs for:
|
||||
- pre_call
|
||||
- during_call
|
||||
- TODO: log post_call. This is more involved since the logs are sent to DD, s3 before the guardrail is even run
|
||||
"""
|
||||
import asyncio
|
||||
import functools
|
||||
|
||||
def process_response(self, response, request_data):
|
||||
self.add_standard_logging_guardrail_information_to_request_data(
|
||||
guardrail_json_response=response,
|
||||
request_data=request_data,
|
||||
guardrail_status="success",
|
||||
)
|
||||
return response
|
||||
|
||||
def process_error(self, e, request_data):
|
||||
self.add_standard_logging_guardrail_information_to_request_data(
|
||||
guardrail_json_response=e,
|
||||
request_data=request_data,
|
||||
guardrail_status="failure",
|
||||
)
|
||||
raise e
|
||||
|
||||
@functools.wraps(func)
|
||||
async def async_wrapper(*args, **kwargs):
|
||||
self: CustomGuardrail = args[0]
|
||||
request_data: Optional[dict] = (
|
||||
kwargs.get("data") or kwargs.get("request_data") or {}
|
||||
)
|
||||
try:
|
||||
response = await func(*args, **kwargs)
|
||||
return process_response(self, response, request_data)
|
||||
except Exception as e:
|
||||
return process_error(self, e, request_data)
|
||||
|
||||
@functools.wraps(func)
|
||||
def sync_wrapper(*args, **kwargs):
|
||||
self: CustomGuardrail = args[0]
|
||||
request_data: Optional[dict] = (
|
||||
kwargs.get("data") or kwargs.get("request_data") or {}
|
||||
)
|
||||
try:
|
||||
response = func(*args, **kwargs)
|
||||
return process_response(self, response, request_data)
|
||||
except Exception as e:
|
||||
return process_error(self, e, request_data)
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if asyncio.iscoroutinefunction(func):
|
||||
return async_wrapper(*args, **kwargs)
|
||||
return sync_wrapper(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ class OpenTelemetry(CustomLogger):
|
|||
from opentelemetry import trace
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.trace import SpanKind
|
||||
|
||||
if config is None:
|
||||
config = OpenTelemetryConfig.from_env()
|
||||
|
|
@ -99,6 +100,8 @@ class OpenTelemetry(CustomLogger):
|
|||
trace.set_tracer_provider(provider)
|
||||
self.tracer = trace.get_tracer(LITELLM_TRACER_NAME)
|
||||
|
||||
self.span_kind = SpanKind
|
||||
|
||||
_debug_otel = str(os.getenv("DEBUG_OTEL", "False")).lower()
|
||||
|
||||
if _debug_otel == "true":
|
||||
|
|
|
|||
BIN
litellm/litellm_core_utils/audio_utils/audio_health_check.wav
Normal file
BIN
litellm/litellm_core_utils/audio_utils/audio_health_check.wav
Normal file
Binary file not shown.
|
|
@ -2,6 +2,8 @@
|
|||
Utils used for litellm.transcription() and litellm.atranscription()
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from litellm.types.utils import FileTypes
|
||||
|
||||
|
||||
|
|
@ -21,3 +23,14 @@ def get_audio_file_name(file_obj: FileTypes) -> str:
|
|||
return str(file_obj)
|
||||
else:
|
||||
return repr(file_obj)
|
||||
|
||||
|
||||
def get_audio_file_for_health_check() -> FileTypes:
|
||||
"""
|
||||
Get an audio file for health check
|
||||
|
||||
Returns the content of `audio_health_check.wav` in the same directory as this file
|
||||
"""
|
||||
pwd = os.path.dirname(os.path.realpath(__file__))
|
||||
file_path = os.path.join(pwd, "audio_health_check.wav")
|
||||
return open(file_path, "rb")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import json
|
||||
import traceback
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import httpx
|
||||
|
||||
|
|
@ -84,6 +84,41 @@ def _get_response_headers(original_exception: Exception) -> Optional[httpx.Heade
|
|||
return _response_headers
|
||||
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def extract_and_raise_litellm_exception(
|
||||
response: Optional[Any],
|
||||
error_str: str,
|
||||
model: str,
|
||||
custom_llm_provider: str,
|
||||
):
|
||||
"""
|
||||
Covers scenario where litellm sdk calling proxy.
|
||||
|
||||
Enables raising the special errors raised by litellm, eg. ContextWindowExceededError.
|
||||
|
||||
Relevant Issue: https://github.com/BerriAI/litellm/issues/7259
|
||||
"""
|
||||
pattern = r"litellm\.\w+Error"
|
||||
|
||||
# Search for the exception in the error string
|
||||
match = re.search(pattern, error_str)
|
||||
|
||||
# Extract the exception if found
|
||||
if match:
|
||||
exception_name = match.group(0)
|
||||
exception_name = exception_name.strip().replace("litellm.", "")
|
||||
raised_exception_obj = getattr(litellm, exception_name, None)
|
||||
if raised_exception_obj:
|
||||
raise raised_exception_obj(
|
||||
message=error_str,
|
||||
llm_provider=custom_llm_provider,
|
||||
model=model,
|
||||
response=response,
|
||||
)
|
||||
|
||||
|
||||
def exception_type( # type: ignore # noqa: PLR0915
|
||||
model,
|
||||
original_exception,
|
||||
|
|
@ -197,6 +232,15 @@ def exception_type( # type: ignore # noqa: PLR0915
|
|||
litellm_debug_info=extra_information,
|
||||
)
|
||||
|
||||
if (
|
||||
custom_llm_provider == "litellm_proxy"
|
||||
): # handle special case where calling litellm proxy + exception str contains error message
|
||||
extract_and_raise_litellm_exception(
|
||||
response=getattr(original_exception, "response", None),
|
||||
error_str=error_str,
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
if (
|
||||
custom_llm_provider == "openai"
|
||||
or custom_llm_provider == "text-completion-openai"
|
||||
|
|
|
|||
|
|
@ -192,4 +192,12 @@ def get_supported_openai_params( # noqa: PLR0915
|
|||
)
|
||||
else:
|
||||
return litellm.TritonConfig().get_supported_openai_params(model=model)
|
||||
elif custom_llm_provider == "deepgram":
|
||||
if request_type == "transcription":
|
||||
return (
|
||||
litellm.DeepgramAudioTranscriptionConfig().get_supported_openai_params(
|
||||
model=model
|
||||
)
|
||||
)
|
||||
|
||||
return None
|
||||
|
|
|
|||
28
litellm/litellm_core_utils/health_check_utils.py
Normal file
28
litellm/litellm_core_utils/health_check_utils.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
"""
|
||||
Utils used for litellm.ahealth_check()
|
||||
"""
|
||||
|
||||
|
||||
def _filter_model_params(model_params: dict) -> dict:
|
||||
"""Remove 'messages' param from model params."""
|
||||
return {k: v for k, v in model_params.items() if k != "messages"}
|
||||
|
||||
|
||||
def _create_health_check_response(response_headers: dict) -> dict:
|
||||
response = {}
|
||||
|
||||
if (
|
||||
response_headers.get("x-ratelimit-remaining-requests", None) is not None
|
||||
): # not provided for dall-e requests
|
||||
response["x-ratelimit-remaining-requests"] = response_headers[
|
||||
"x-ratelimit-remaining-requests"
|
||||
]
|
||||
|
||||
if response_headers.get("x-ratelimit-remaining-tokens", None) is not None:
|
||||
response["x-ratelimit-remaining-tokens"] = response_headers[
|
||||
"x-ratelimit-remaining-tokens"
|
||||
]
|
||||
|
||||
if response_headers.get("x-ms-region", None) is not None:
|
||||
response["x-ms-region"] = response_headers["x-ms-region"]
|
||||
return response
|
||||
|
|
@ -3038,6 +3038,9 @@ def get_standard_logging_object_payload(
|
|||
response_cost_failure_debug_info=kwargs.get(
|
||||
"response_cost_failure_debug_information"
|
||||
),
|
||||
guardrail_information=metadata.get(
|
||||
"standard_logging_guardrail_information", None
|
||||
),
|
||||
)
|
||||
|
||||
return payload
|
||||
|
|
|
|||
|
|
@ -1491,120 +1491,3 @@ class AzureChatCompletion(BaseLLM):
|
|||
response["x-ms-region"] = completion.headers["x-ms-region"]
|
||||
|
||||
return response
|
||||
|
||||
async def ahealth_check(
|
||||
self,
|
||||
model: Optional[str],
|
||||
api_key: Optional[str],
|
||||
api_base: str,
|
||||
api_version: Optional[str],
|
||||
timeout: float,
|
||||
mode: str,
|
||||
messages: Optional[list] = None,
|
||||
input: Optional[list] = None,
|
||||
prompt: Optional[str] = None,
|
||||
) -> dict:
|
||||
client_session = (
|
||||
litellm.aclient_session
|
||||
or get_async_httpx_client(llm_provider=LlmProviders.AZURE).client
|
||||
) # handle dall-e-2 calls
|
||||
|
||||
if "gateway.ai.cloudflare.com" in api_base:
|
||||
## build base url - assume api base includes resource name
|
||||
if not api_base.endswith("/"):
|
||||
api_base += "/"
|
||||
api_base += f"{model}"
|
||||
client = AsyncAzureOpenAI(
|
||||
base_url=api_base,
|
||||
api_version=api_version,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
http_client=client_session,
|
||||
)
|
||||
model = None
|
||||
# cloudflare ai gateway, needs model=None
|
||||
else:
|
||||
client = AsyncAzureOpenAI(
|
||||
api_version=api_version,
|
||||
azure_endpoint=api_base,
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
http_client=client_session,
|
||||
)
|
||||
|
||||
# only run this check if it's not cloudflare ai gateway
|
||||
if model is None and mode != "image_generation":
|
||||
raise Exception("model is not set")
|
||||
|
||||
completion = None
|
||||
|
||||
if mode == "completion":
|
||||
completion = await client.completions.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "chat":
|
||||
if messages is None:
|
||||
raise Exception("messages is not set")
|
||||
completion = await client.chat.completions.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
messages=messages, # type: ignore
|
||||
)
|
||||
elif mode == "embedding":
|
||||
if input is None:
|
||||
raise Exception("input is not set")
|
||||
completion = await client.embeddings.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
input=input, # type: ignore
|
||||
)
|
||||
elif mode == "image_generation":
|
||||
if prompt is None:
|
||||
raise Exception("prompt is not set")
|
||||
completion = await client.images.with_raw_response.generate(
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "audio_transcription":
|
||||
# Get the current directory of the file being run
|
||||
pwd = os.path.dirname(os.path.realpath(__file__))
|
||||
file_path = os.path.join(
|
||||
pwd, "../../../tests/gettysburg.wav"
|
||||
) # proxy address
|
||||
audio_file = open(file_path, "rb")
|
||||
completion = await client.audio.transcriptions.with_raw_response.create(
|
||||
file=audio_file,
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "audio_speech":
|
||||
# Get the current directory of the file being run
|
||||
completion = await client.audio.speech.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
input=prompt, # type: ignore
|
||||
voice="alloy",
|
||||
)
|
||||
elif mode == "batch":
|
||||
completion = await client.batches.with_raw_response.list(limit=1) # type: ignore
|
||||
else:
|
||||
raise Exception("mode not set")
|
||||
response = {}
|
||||
|
||||
if completion is None or not hasattr(completion, "headers"):
|
||||
raise Exception("invalid completion response")
|
||||
|
||||
if (
|
||||
completion.headers.get("x-ratelimit-remaining-requests", None) is not None
|
||||
): # not provided for dall-e requests
|
||||
response["x-ratelimit-remaining-requests"] = completion.headers[
|
||||
"x-ratelimit-remaining-requests"
|
||||
]
|
||||
|
||||
if completion.headers.get("x-ratelimit-remaining-tokens", None) is not None:
|
||||
response["x-ratelimit-remaining-tokens"] = completion.headers[
|
||||
"x-ratelimit-remaining-tokens"
|
||||
]
|
||||
|
||||
if completion.headers.get("x-ms-region", None) is not None:
|
||||
response["x-ms-region"] = completion.headers["x-ms-region"]
|
||||
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -1,179 +1,48 @@
|
|||
from typing import Any, Coroutine, Optional, Union
|
||||
from typing import Optional, Union
|
||||
|
||||
import httpx
|
||||
from openai import AsyncAzureOpenAI, AzureOpenAI
|
||||
from openai.types.fine_tuning import FineTuningJob
|
||||
from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
from litellm.llms.azure.files.handler import get_azure_openai_client
|
||||
from litellm.llms.base import BaseLLM
|
||||
from litellm.llms.openai.fine_tuning.handler import OpenAIFineTuningAPI
|
||||
|
||||
|
||||
class AzureOpenAIFineTuningAPI(BaseLLM):
|
||||
class AzureOpenAIFineTuningAPI(OpenAIFineTuningAPI):
|
||||
"""
|
||||
AzureOpenAI methods to support for batches
|
||||
AzureOpenAI methods to support fine tuning, inherits from OpenAIFineTuningAPI.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
async def acreate_fine_tuning_job(
|
||||
def get_openai_client(
|
||||
self,
|
||||
create_fine_tuning_job_data: dict,
|
||||
openai_client: AsyncAzureOpenAI,
|
||||
) -> FineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.create(
|
||||
**create_fine_tuning_job_data # type: ignore
|
||||
)
|
||||
return response
|
||||
|
||||
def create_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
create_fine_tuning_job_data: dict,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str] = None,
|
||||
client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None,
|
||||
organization: Optional[str],
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
_is_async: bool = False,
|
||||
api_version: Optional[str] = None,
|
||||
) -> Union[FineTuningJob, Coroutine[Any, Any, FineTuningJob]]:
|
||||
openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = (
|
||||
get_azure_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
api_version=api_version,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
)
|
||||
) -> Optional[
|
||||
Union[
|
||||
OpenAI,
|
||||
AsyncOpenAI,
|
||||
AzureOpenAI,
|
||||
AsyncAzureOpenAI,
|
||||
]
|
||||
]:
|
||||
# Override to use Azure-specific client initialization
|
||||
if isinstance(client, OpenAI) or isinstance(client, AsyncOpenAI):
|
||||
client = None
|
||||
|
||||
return get_azure_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
api_version=api_version,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncAzureOpenAI):
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not an instance of AsyncAzureOpenAI. Make sure you passed an AsyncAzureOpenAI client."
|
||||
)
|
||||
return self.acreate_fine_tuning_job( # type: ignore
|
||||
create_fine_tuning_job_data=create_fine_tuning_job_data,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
verbose_logger.debug(
|
||||
"creating fine tuning job, args= %s", create_fine_tuning_job_data
|
||||
)
|
||||
response = openai_client.fine_tuning.jobs.create(**create_fine_tuning_job_data) # type: ignore
|
||||
return response
|
||||
|
||||
async def acancel_fine_tuning_job(
|
||||
self,
|
||||
fine_tuning_job_id: str,
|
||||
openai_client: AsyncAzureOpenAI,
|
||||
) -> FineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.cancel(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return response
|
||||
|
||||
def cancel_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
fine_tuning_job_id: str,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None,
|
||||
):
|
||||
openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = (
|
||||
get_azure_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
api_version=api_version,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
)
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncAzureOpenAI):
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not an instance of AsyncAzureOpenAI. Make sure you passed an AsyncAzureOpenAI client."
|
||||
)
|
||||
return self.acancel_fine_tuning_job( # type: ignore
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
verbose_logger.debug("canceling fine tuning job, args= %s", fine_tuning_job_id)
|
||||
response = openai_client.fine_tuning.jobs.cancel(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return response
|
||||
|
||||
async def alist_fine_tuning_jobs(
|
||||
self,
|
||||
openai_client: AsyncAzureOpenAI,
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
):
|
||||
response = await openai_client.fine_tuning.jobs.list(after=after, limit=limit) # type: ignore
|
||||
return response
|
||||
|
||||
def list_fine_tuning_jobs(
|
||||
self,
|
||||
_is_async: bool,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str] = None,
|
||||
client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = None,
|
||||
api_version: Optional[str] = None,
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
):
|
||||
openai_client: Optional[Union[AzureOpenAI, AsyncAzureOpenAI]] = (
|
||||
get_azure_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
api_version=api_version,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
)
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncAzureOpenAI):
|
||||
raise ValueError(
|
||||
"AzureOpenAI client is not an instance of AsyncAzureOpenAI. Make sure you passed an AsyncAzureOpenAI client."
|
||||
)
|
||||
return self.alist_fine_tuning_jobs( # type: ignore
|
||||
after=after,
|
||||
limit=limit,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
verbose_logger.debug("list fine tuning job, after= %s, limit= %s", after, limit)
|
||||
response = openai_client.fine_tuning.jobs.list(after=after, limit=limit) # type: ignore
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class AzureAIStudioConfig(OpenAIConfig):
|
|||
message["content"] = texts
|
||||
return messages
|
||||
|
||||
def _is_azure_openai_model(self, model: str) -> bool:
|
||||
def _is_azure_openai_model(self, model: str, api_base: Optional[str]) -> bool:
|
||||
try:
|
||||
if "/" in model:
|
||||
model = model.split("/", 1)[1]
|
||||
|
|
@ -61,6 +61,9 @@ class AzureAIStudioConfig(OpenAIConfig):
|
|||
or model in litellm.open_ai_embedding_models
|
||||
):
|
||||
return True
|
||||
|
||||
if api_base and "services.ai.azure" in api_base:
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
|
@ -75,7 +78,7 @@ class AzureAIStudioConfig(OpenAIConfig):
|
|||
api_base = api_base or get_secret_str("AZURE_AI_API_BASE")
|
||||
dynamic_api_key = api_key or get_secret_str("AZURE_AI_API_KEY")
|
||||
|
||||
if self._is_azure_openai_model(model=model):
|
||||
if self._is_azure_openai_model(model=model, api_base=api_base):
|
||||
verbose_logger.debug(
|
||||
"Model={} is Azure OpenAI model. Setting custom_llm_provider='azure'.".format(
|
||||
model
|
||||
|
|
|
|||
9
litellm/llms/base_llm/base_utils.py
Normal file
9
litellm/llms/base_llm/base_utils.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from litellm.types.utils import ModelInfoBase
|
||||
|
||||
|
||||
class BaseLLMModelInfo(ABC):
|
||||
@abstractmethod
|
||||
def get_model_info(self, model: str) -> ModelInfoBase:
|
||||
pass
|
||||
|
|
@ -3,27 +3,26 @@ Common utilities used across bedrock chat/embedding/image generation
|
|||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import types
|
||||
from enum import Enum
|
||||
from typing import List, Optional, Union
|
||||
from typing import Any, List, Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
import litellm
|
||||
from litellm.llms.base_llm.chat.transformation import (
|
||||
BaseConfig,
|
||||
BaseLLMException,
|
||||
LiteLLMLoggingObj,
|
||||
)
|
||||
from litellm.secret_managers.main import get_secret
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
from litellm.types.utils import ModelResponse
|
||||
|
||||
|
||||
class BedrockError(Exception):
|
||||
def __init__(self, status_code, message):
|
||||
self.status_code = status_code
|
||||
self.message = message
|
||||
self.request = httpx.Request(
|
||||
method="POST", url="https://us-west-2.console.aws.amazon.com/bedrock"
|
||||
)
|
||||
self.response = httpx.Response(status_code=status_code, request=self.request)
|
||||
super().__init__(
|
||||
self.message
|
||||
) # Call the base class constructor with the parameters it needs
|
||||
class BedrockError(BaseLLMException):
|
||||
pass
|
||||
|
||||
|
||||
class AmazonBedrockGlobalConfig:
|
||||
|
|
@ -65,7 +64,64 @@ class AmazonBedrockGlobalConfig:
|
|||
]
|
||||
|
||||
|
||||
class AmazonTitanConfig:
|
||||
class AmazonInvokeMixin:
|
||||
"""
|
||||
Base class for bedrock models going through invoke_handler.py
|
||||
"""
|
||||
|
||||
def get_error_class(
|
||||
self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers]
|
||||
) -> BaseLLMException:
|
||||
return BedrockError(
|
||||
message=error_message,
|
||||
status_code=status_code,
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
def transform_request(
|
||||
self,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
headers: dict,
|
||||
) -> dict:
|
||||
raise NotImplementedError(
|
||||
"transform_request not implemented for config. Done in invoke_handler.py"
|
||||
)
|
||||
|
||||
def transform_response(
|
||||
self,
|
||||
model: str,
|
||||
raw_response: httpx.Response,
|
||||
model_response: ModelResponse,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
request_data: dict,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
encoding: Any,
|
||||
api_key: Optional[str] = None,
|
||||
json_mode: Optional[bool] = None,
|
||||
) -> ModelResponse:
|
||||
raise NotImplementedError(
|
||||
"transform_response not implemented for config. Done in invoke_handler.py"
|
||||
)
|
||||
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
api_key: Optional[str] = None,
|
||||
) -> dict:
|
||||
raise NotImplementedError(
|
||||
"validate_environment not implemented for config. Done in invoke_handler.py"
|
||||
)
|
||||
|
||||
|
||||
class AmazonTitanConfig(AmazonInvokeMixin, BaseConfig):
|
||||
"""
|
||||
Reference: https://us-west-2.console.aws.amazon.com/bedrock/home?region=us-west-2#/providers?model=titan-text-express-v1
|
||||
|
||||
|
|
@ -100,6 +156,7 @@ class AmazonTitanConfig:
|
|||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
if not k.startswith("__")
|
||||
and not k.startswith("_abc")
|
||||
and not isinstance(
|
||||
v,
|
||||
(
|
||||
|
|
@ -112,6 +169,62 @@ class AmazonTitanConfig:
|
|||
and v is not None
|
||||
}
|
||||
|
||||
def _map_and_modify_arg(
|
||||
self,
|
||||
supported_params: dict,
|
||||
provider: str,
|
||||
model: str,
|
||||
stop: Union[List[str], str],
|
||||
):
|
||||
"""
|
||||
filter params to fit the required provider format, drop those that don't fit if user sets `litellm.drop_params = True`.
|
||||
"""
|
||||
filtered_stop = None
|
||||
if "stop" in supported_params and litellm.drop_params:
|
||||
if provider == "bedrock" and "amazon" in model:
|
||||
filtered_stop = []
|
||||
if isinstance(stop, list):
|
||||
for s in stop:
|
||||
if re.match(r"^(\|+|User:)$", s):
|
||||
filtered_stop.append(s)
|
||||
if filtered_stop is not None:
|
||||
supported_params["stop"] = filtered_stop
|
||||
|
||||
return supported_params
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List[str]:
|
||||
return [
|
||||
"max_tokens",
|
||||
"max_completion_tokens",
|
||||
"stop",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"stream",
|
||||
]
|
||||
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
for k, v in non_default_params.items():
|
||||
if k == "max_tokens" or k == "max_completion_tokens":
|
||||
optional_params["maxTokenCount"] = v
|
||||
if k == "temperature":
|
||||
optional_params["temperature"] = v
|
||||
if k == "stop":
|
||||
filtered_stop = self._map_and_modify_arg(
|
||||
{"stop": v}, provider="bedrock", model=model, stop=v
|
||||
)
|
||||
optional_params["stopSequences"] = filtered_stop["stop"]
|
||||
if k == "top_p":
|
||||
optional_params["topP"] = v
|
||||
if k == "stream":
|
||||
optional_params["stream"] = v
|
||||
return optional_params
|
||||
|
||||
|
||||
class AmazonAnthropicClaude3Config:
|
||||
"""
|
||||
|
|
@ -276,7 +389,7 @@ class AmazonAnthropicConfig:
|
|||
return optional_params
|
||||
|
||||
|
||||
class AmazonCohereConfig:
|
||||
class AmazonCohereConfig(AmazonInvokeMixin, BaseConfig):
|
||||
"""
|
||||
Reference: https://us-west-2.console.aws.amazon.com/bedrock/home?region=us-west-2#/providers?model=command
|
||||
|
||||
|
|
@ -308,6 +421,7 @@ class AmazonCohereConfig:
|
|||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
if not k.startswith("__")
|
||||
and not k.startswith("_abc")
|
||||
and not isinstance(
|
||||
v,
|
||||
(
|
||||
|
|
@ -320,8 +434,31 @@ class AmazonCohereConfig:
|
|||
and v is not None
|
||||
}
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List[str]:
|
||||
return [
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"stream",
|
||||
]
|
||||
|
||||
class AmazonAI21Config:
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
for k, v in non_default_params.items():
|
||||
if k == "stream":
|
||||
optional_params["stream"] = v
|
||||
if k == "temperature":
|
||||
optional_params["temperature"] = v
|
||||
if k == "max_tokens":
|
||||
optional_params["max_tokens"] = v
|
||||
return optional_params
|
||||
|
||||
|
||||
class AmazonAI21Config(AmazonInvokeMixin, BaseConfig):
|
||||
"""
|
||||
Reference: https://us-west-2.console.aws.amazon.com/bedrock/home?region=us-west-2#/providers?model=j2-ultra
|
||||
|
||||
|
|
@ -371,6 +508,7 @@ class AmazonAI21Config:
|
|||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
if not k.startswith("__")
|
||||
and not k.startswith("_abc")
|
||||
and not isinstance(
|
||||
v,
|
||||
(
|
||||
|
|
@ -383,13 +521,39 @@ class AmazonAI21Config:
|
|||
and v is not None
|
||||
}
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List:
|
||||
return [
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"stream",
|
||||
]
|
||||
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
for k, v in non_default_params.items():
|
||||
if k == "max_tokens":
|
||||
optional_params["maxTokens"] = v
|
||||
if k == "temperature":
|
||||
optional_params["temperature"] = v
|
||||
if k == "top_p":
|
||||
optional_params["topP"] = v
|
||||
if k == "stream":
|
||||
optional_params["stream"] = v
|
||||
return optional_params
|
||||
|
||||
|
||||
class AnthropicConstants(Enum):
|
||||
HUMAN_PROMPT = "\n\nHuman: "
|
||||
AI_PROMPT = "\n\nAssistant: "
|
||||
|
||||
|
||||
class AmazonLlamaConfig:
|
||||
class AmazonLlamaConfig(AmazonInvokeMixin, BaseConfig):
|
||||
"""
|
||||
Reference: https://us-west-2.console.aws.amazon.com/bedrock/home?region=us-west-2#/providers?model=meta.llama2-13b-chat-v1
|
||||
|
||||
|
|
@ -421,6 +585,7 @@ class AmazonLlamaConfig:
|
|||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
if not k.startswith("__")
|
||||
and not k.startswith("_abc")
|
||||
and not isinstance(
|
||||
v,
|
||||
(
|
||||
|
|
@ -433,8 +598,34 @@ class AmazonLlamaConfig:
|
|||
and v is not None
|
||||
}
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List:
|
||||
return [
|
||||
"max_tokens",
|
||||
"temperature",
|
||||
"top_p",
|
||||
"stream",
|
||||
]
|
||||
|
||||
class AmazonMistralConfig:
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
for k, v in non_default_params.items():
|
||||
if k == "max_tokens":
|
||||
optional_params["max_gen_len"] = v
|
||||
if k == "temperature":
|
||||
optional_params["temperature"] = v
|
||||
if k == "top_p":
|
||||
optional_params["top_p"] = v
|
||||
if k == "stream":
|
||||
optional_params["stream"] = v
|
||||
return optional_params
|
||||
|
||||
|
||||
class AmazonMistralConfig(AmazonInvokeMixin, BaseConfig):
|
||||
"""
|
||||
Reference: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-mistral.html
|
||||
Supported Params for the Amazon / Mistral models:
|
||||
|
|
@ -471,6 +662,7 @@ class AmazonMistralConfig:
|
|||
k: v
|
||||
for k, v in cls.__dict__.items()
|
||||
if not k.startswith("__")
|
||||
and not k.startswith("_abc")
|
||||
and not isinstance(
|
||||
v,
|
||||
(
|
||||
|
|
@ -483,6 +675,29 @@ class AmazonMistralConfig:
|
|||
and v is not None
|
||||
}
|
||||
|
||||
def get_supported_openai_params(self, model: str) -> List[str]:
|
||||
return ["max_tokens", "temperature", "top_p", "stop", "stream"]
|
||||
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
for k, v in non_default_params.items():
|
||||
if k == "max_tokens":
|
||||
optional_params["max_tokens"] = v
|
||||
if k == "temperature":
|
||||
optional_params["temperature"] = v
|
||||
if k == "top_p":
|
||||
optional_params["top_p"] = v
|
||||
if k == "stop":
|
||||
optional_params["stop"] = v
|
||||
if k == "stream":
|
||||
optional_params["stream"] = v
|
||||
return optional_params
|
||||
|
||||
|
||||
def add_custom_header(headers):
|
||||
"""Closure to capture the headers and add them."""
|
||||
|
|
|
|||
|
|
@ -492,16 +492,18 @@ class HTTPHandler:
|
|||
headers: Optional[dict] = None,
|
||||
stream: bool = False,
|
||||
timeout: Optional[Union[float, httpx.Timeout]] = None,
|
||||
files: Optional[dict] = None,
|
||||
content: Any = None,
|
||||
):
|
||||
try:
|
||||
|
||||
if timeout is not None:
|
||||
req = self.client.build_request(
|
||||
"POST", url, data=data, json=json, params=params, headers=headers, timeout=timeout # type: ignore
|
||||
"POST", url, data=data, json=json, params=params, headers=headers, timeout=timeout, files=files, content=content # type: ignore
|
||||
)
|
||||
else:
|
||||
req = self.client.build_request(
|
||||
"POST", url, data=data, json=json, params=params, headers=headers # type: ignore
|
||||
"POST", url, data=data, json=json, params=params, headers=headers, files=files, content=content # type: ignore
|
||||
)
|
||||
response = self.client.send(req, stream=stream)
|
||||
response.raise_for_status()
|
||||
|
|
@ -513,7 +515,6 @@ class HTTPHandler:
|
|||
llm_provider="litellm-httpx-handler",
|
||||
)
|
||||
except httpx.HTTPStatusError as e:
|
||||
|
||||
if stream is True:
|
||||
setattr(e, "message", mask_sensitive_info(e.response.read()))
|
||||
setattr(e, "text", mask_sensitive_info(e.response.read()))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import io
|
||||
import json
|
||||
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union
|
||||
|
||||
|
|
@ -17,7 +18,7 @@ from litellm.llms.custom_httpx.http_handler import (
|
|||
get_async_httpx_client,
|
||||
)
|
||||
from litellm.types.rerank import OptionalRerankParams, RerankResponse
|
||||
from litellm.types.utils import EmbeddingResponse
|
||||
from litellm.types.utils import EmbeddingResponse, FileTypes, TranscriptionResponse
|
||||
from litellm.utils import CustomStreamWrapper, ModelResponse, ProviderConfigManager
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -667,6 +668,115 @@ class BaseLLMHTTPHandler:
|
|||
request_data=request_data,
|
||||
)
|
||||
|
||||
def handle_audio_file(self, audio_file: FileTypes) -> bytes:
|
||||
"""
|
||||
Processes the audio file input based on its type and returns the binary data.
|
||||
|
||||
Args:
|
||||
audio_file: Can be a file path (str), a tuple (filename, file_content), or binary data (bytes).
|
||||
|
||||
Returns:
|
||||
The binary data of the audio file.
|
||||
"""
|
||||
binary_data: bytes # Explicitly declare the type
|
||||
|
||||
# Handle the audio file based on type
|
||||
if isinstance(audio_file, str):
|
||||
# If it's a file path
|
||||
with open(audio_file, "rb") as f:
|
||||
binary_data = f.read() # `f.read()` always returns `bytes`
|
||||
elif isinstance(audio_file, tuple):
|
||||
# Handle tuple case
|
||||
_, file_content = audio_file[:2]
|
||||
if isinstance(file_content, str):
|
||||
with open(file_content, "rb") as f:
|
||||
binary_data = f.read() # `f.read()` always returns `bytes`
|
||||
elif isinstance(file_content, bytes):
|
||||
binary_data = file_content
|
||||
else:
|
||||
raise TypeError(
|
||||
f"Unexpected type in tuple: {type(file_content)}. Expected str or bytes."
|
||||
)
|
||||
elif isinstance(audio_file, bytes):
|
||||
# Assume it's already binary data
|
||||
binary_data = audio_file
|
||||
elif isinstance(audio_file, io.BufferedReader):
|
||||
# Handle file-like objects
|
||||
binary_data = audio_file.read()
|
||||
|
||||
else:
|
||||
raise TypeError(f"Unsupported type for audio_file: {type(audio_file)}")
|
||||
|
||||
return binary_data
|
||||
|
||||
def audio_transcriptions(
|
||||
self,
|
||||
model: str,
|
||||
audio_file: FileTypes,
|
||||
optional_params: dict,
|
||||
model_response: TranscriptionResponse,
|
||||
timeout: float,
|
||||
max_retries: int,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
custom_llm_provider: str,
|
||||
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
|
||||
atranscription: bool = False,
|
||||
headers: dict = {},
|
||||
) -> TranscriptionResponse:
|
||||
provider_config = ProviderConfigManager.get_provider_audio_transcription_config(
|
||||
model=model, provider=litellm.LlmProviders(custom_llm_provider)
|
||||
)
|
||||
if provider_config is None:
|
||||
raise ValueError(
|
||||
f"No provider config found for model: {model} and provider: {custom_llm_provider}"
|
||||
)
|
||||
headers = provider_config.validate_environment(
|
||||
api_key=api_key,
|
||||
headers=headers,
|
||||
model=model,
|
||||
messages=[],
|
||||
optional_params=optional_params,
|
||||
)
|
||||
|
||||
if client is None or not isinstance(client, HTTPHandler):
|
||||
client = _get_httpx_client()
|
||||
|
||||
complete_url = provider_config.get_complete_url(
|
||||
api_base=api_base,
|
||||
model=model,
|
||||
optional_params=optional_params,
|
||||
)
|
||||
|
||||
# Handle the audio file based on type
|
||||
binary_data = self.handle_audio_file(audio_file)
|
||||
|
||||
try:
|
||||
# Make the POST request
|
||||
response = client.post(
|
||||
url=complete_url,
|
||||
headers=headers,
|
||||
content=binary_data,
|
||||
timeout=timeout,
|
||||
)
|
||||
except Exception as e:
|
||||
raise self._handle_error(e=e, provider_config=provider_config)
|
||||
|
||||
if isinstance(provider_config, litellm.DeepgramAudioTranscriptionConfig):
|
||||
returned_response = provider_config.transform_audio_transcription_response(
|
||||
model=model,
|
||||
raw_response=response,
|
||||
model_response=model_response,
|
||||
logging_obj=logging_obj,
|
||||
request_data={},
|
||||
optional_params=optional_params,
|
||||
litellm_params={},
|
||||
api_key=api_key,
|
||||
)
|
||||
return returned_response
|
||||
return model_response
|
||||
|
||||
def _handle_error(
|
||||
self, e: Exception, provider_config: Union[BaseConfig, BaseRerankConfig]
|
||||
):
|
||||
|
|
|
|||
125
litellm/llms/deepgram/audio_transcription/transformation.py
Normal file
125
litellm/llms/deepgram/audio_transcription/transformation.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
"""
|
||||
Translates from OpenAI's `/v1/audio/transcriptions` to Deepgram's `/v1/listen`
|
||||
"""
|
||||
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from httpx import Headers, Response
|
||||
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.types.llms.openai import (
|
||||
AllMessageValues,
|
||||
OpenAIAudioTranscriptionOptionalParams,
|
||||
)
|
||||
from litellm.types.utils import TranscriptionResponse
|
||||
|
||||
from ...base_llm.audio_transcription.transformation import (
|
||||
BaseAudioTranscriptionConfig,
|
||||
LiteLLMLoggingObj,
|
||||
)
|
||||
from ..common_utils import DeepgramException
|
||||
|
||||
|
||||
class DeepgramAudioTranscriptionConfig(BaseAudioTranscriptionConfig):
|
||||
def get_supported_openai_params(
|
||||
self, model: str
|
||||
) -> List[OpenAIAudioTranscriptionOptionalParams]:
|
||||
return ["language"]
|
||||
|
||||
def map_openai_params(
|
||||
self,
|
||||
non_default_params: dict,
|
||||
optional_params: dict,
|
||||
model: str,
|
||||
drop_params: bool,
|
||||
) -> dict:
|
||||
supported_params = self.get_supported_openai_params(model)
|
||||
for k, v in non_default_params.items():
|
||||
if k in supported_params:
|
||||
optional_params[k] = v
|
||||
return optional_params
|
||||
|
||||
def get_error_class(
|
||||
self, error_message: str, status_code: int, headers: Union[dict, Headers]
|
||||
) -> BaseLLMException:
|
||||
return DeepgramException(
|
||||
message=error_message, status_code=status_code, headers=headers
|
||||
)
|
||||
|
||||
def transform_audio_transcription_response(
|
||||
self,
|
||||
model: str,
|
||||
raw_response: Response,
|
||||
model_response: TranscriptionResponse,
|
||||
logging_obj: LiteLLMLoggingObj,
|
||||
request_data: dict,
|
||||
optional_params: dict,
|
||||
litellm_params: dict,
|
||||
api_key: Optional[str] = None,
|
||||
) -> TranscriptionResponse:
|
||||
"""
|
||||
Transforms the raw response from Deepgram to the TranscriptionResponse format
|
||||
"""
|
||||
try:
|
||||
response_json = raw_response.json()
|
||||
|
||||
# Get the first alternative from the first channel
|
||||
first_channel = response_json["results"]["channels"][0]
|
||||
first_alternative = first_channel["alternatives"][0]
|
||||
|
||||
# Extract the full transcript
|
||||
text = first_alternative["transcript"]
|
||||
|
||||
# Create TranscriptionResponse object
|
||||
response = TranscriptionResponse(text=text)
|
||||
|
||||
# Add additional metadata matching OpenAI format
|
||||
response["task"] = "transcribe"
|
||||
response["language"] = (
|
||||
"english" # Deepgram auto-detects but doesn't return language
|
||||
)
|
||||
response["duration"] = response_json["metadata"]["duration"]
|
||||
|
||||
# Transform words to match OpenAI format
|
||||
if "words" in first_alternative:
|
||||
response["words"] = [
|
||||
{"word": word["word"], "start": word["start"], "end": word["end"]}
|
||||
for word in first_alternative["words"]
|
||||
]
|
||||
|
||||
# Store full response in hidden params
|
||||
response._hidden_params = response_json
|
||||
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
f"Error transforming Deepgram response: {str(e)}\nResponse: {raw_response.text}"
|
||||
)
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
api_base: Optional[str],
|
||||
model: str,
|
||||
optional_params: dict,
|
||||
stream: Optional[bool] = None,
|
||||
) -> str:
|
||||
if api_base is None:
|
||||
api_base = "https://api.deepgram.com/v1"
|
||||
api_base = api_base.rstrip("/") # Remove trailing slash if present
|
||||
|
||||
return f"{api_base}/listen?model={model}"
|
||||
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict,
|
||||
model: str,
|
||||
messages: List[AllMessageValues],
|
||||
optional_params: dict,
|
||||
api_key: Optional[str] = None,
|
||||
) -> dict:
|
||||
api_key = api_key or get_secret_str("DEEPGRAM_API_KEY")
|
||||
return {
|
||||
"Authorization": f"Token {api_key}",
|
||||
}
|
||||
5
litellm/llms/deepgram/common_utils.py
Normal file
5
litellm/llms/deepgram/common_utils.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
||||
|
||||
class DeepgramException(BaseLLMException):
|
||||
pass
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
from typing import List, Literal, Optional, Tuple, Union
|
||||
from typing import List, Literal, Optional, Tuple, Union, cast
|
||||
|
||||
import litellm
|
||||
from litellm.llms.base_llm.base_utils import BaseLLMModelInfo
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.types.llms.openai import AllMessageValues
|
||||
from litellm.types.llms.openai import AllMessageValues, ChatCompletionImageObject
|
||||
from litellm.types.utils import ModelInfoBase, ProviderSpecificModelInfo
|
||||
|
||||
from ...openai.chat.gpt_transformation import OpenAIGPTConfig
|
||||
|
||||
|
||||
class FireworksAIConfig(OpenAIGPTConfig):
|
||||
class FireworksAIConfig(BaseLLMModelInfo, OpenAIGPTConfig):
|
||||
"""
|
||||
Reference: https://docs.fireworks.ai/api-reference/post-chatcompletions
|
||||
|
||||
|
|
@ -110,6 +113,80 @@ class FireworksAIConfig(OpenAIGPTConfig):
|
|||
optional_params[param] = value
|
||||
return optional_params
|
||||
|
||||
def _add_transform_inline_image_block(
|
||||
self,
|
||||
content: ChatCompletionImageObject,
|
||||
model: str,
|
||||
disable_add_transform_inline_image_block: Optional[bool],
|
||||
) -> ChatCompletionImageObject:
|
||||
"""
|
||||
Add transform_inline to the image_url (allows non-vision models to parse documents/images/etc.)
|
||||
- ignore if model is a vision model
|
||||
- ignore if user has disabled this feature
|
||||
"""
|
||||
if (
|
||||
"vision" in model or disable_add_transform_inline_image_block
|
||||
): # allow user to toggle this feature.
|
||||
return content
|
||||
if isinstance(content["image_url"], str):
|
||||
content["image_url"] = f"{content['image_url']}#transform=inline"
|
||||
elif isinstance(content["image_url"], dict):
|
||||
content["image_url"][
|
||||
"url"
|
||||
] = f"{content['image_url']['url']}#transform=inline"
|
||||
return content
|
||||
|
||||
def _transform_messages_helper(
|
||||
self, messages: List[AllMessageValues], model: str, litellm_params: dict
|
||||
) -> List[AllMessageValues]:
|
||||
"""
|
||||
Add 'transform=inline' to the url of the image_url
|
||||
"""
|
||||
disable_add_transform_inline_image_block = cast(
|
||||
Optional[bool],
|
||||
litellm_params.get(
|
||||
"disable_add_transform_inline_image_block",
|
||||
litellm.disable_add_transform_inline_image_block,
|
||||
),
|
||||
)
|
||||
for message in messages:
|
||||
if message["role"] == "user":
|
||||
_message_content = message.get("content")
|
||||
if _message_content is not None and isinstance(_message_content, list):
|
||||
for content in _message_content:
|
||||
if content["type"] == "image_url":
|
||||
content = self._add_transform_inline_image_block(
|
||||
content=content,
|
||||
model=model,
|
||||
disable_add_transform_inline_image_block=disable_add_transform_inline_image_block,
|
||||
)
|
||||
return messages
|
||||
|
||||
def get_model_info(
|
||||
self, model: str, existing_model_info: Optional[ModelInfoBase] = None
|
||||
) -> ModelInfoBase:
|
||||
provider_specific_model_info = ProviderSpecificModelInfo(
|
||||
supports_function_calling=True,
|
||||
supports_prompt_caching=True, # https://docs.fireworks.ai/guides/prompt-caching
|
||||
supports_pdf_input=True, # via document inlining
|
||||
supports_vision=True, # via document inlining
|
||||
)
|
||||
if existing_model_info is not None:
|
||||
return ModelInfoBase(
|
||||
**{**existing_model_info, **provider_specific_model_info}
|
||||
)
|
||||
return ModelInfoBase(
|
||||
key=model,
|
||||
litellm_provider="fireworks_ai",
|
||||
mode="chat",
|
||||
input_cost_per_token=0.0,
|
||||
output_cost_per_token=0.0,
|
||||
max_tokens=None,
|
||||
max_input_tokens=None,
|
||||
max_output_tokens=None,
|
||||
**provider_specific_model_info,
|
||||
)
|
||||
|
||||
def transform_request(
|
||||
self,
|
||||
model: str,
|
||||
|
|
@ -120,6 +197,9 @@ class FireworksAIConfig(OpenAIGPTConfig):
|
|||
) -> dict:
|
||||
if not model.startswith("accounts/"):
|
||||
model = f"accounts/fireworks/models/{model}"
|
||||
messages = self._transform_messages_helper(
|
||||
messages=messages, model=model, litellm_params=litellm_params
|
||||
)
|
||||
return super().transform_request(
|
||||
model=model,
|
||||
messages=messages,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from typing import Dict, List, Optional
|
||||
|
||||
import litellm
|
||||
from litellm.litellm_core_utils.prompt_templates.factory import (
|
||||
convert_generic_image_chunk_to_openai_image_obj,
|
||||
convert_to_anthropic_image_obj,
|
||||
|
|
@ -96,6 +97,8 @@ class GoogleAIStudioGeminiConfig(
|
|||
del non_default_params["frequency_penalty"]
|
||||
if "presence_penalty" in non_default_params:
|
||||
del non_default_params["presence_penalty"]
|
||||
if litellm.vertex_ai_safety_settings is not None:
|
||||
optional_params["safety_settings"] = litellm.vertex_ai_safety_settings
|
||||
return super().map_openai_params(
|
||||
model=model,
|
||||
non_default_params=non_default_params,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Any, Coroutine, Optional, Union
|
||||
|
||||
import httpx
|
||||
from openai import AsyncOpenAI, OpenAI
|
||||
from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
|
||||
from openai.types.fine_tuning import FineTuningJob
|
||||
|
||||
from litellm._logging import verbose_logger
|
||||
|
|
@ -22,11 +22,23 @@ class OpenAIFineTuningAPI:
|
|||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[Union[OpenAI, AsyncOpenAI]] = None,
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
_is_async: bool = False,
|
||||
) -> Optional[Union[OpenAI, AsyncOpenAI]]:
|
||||
api_version: Optional[str] = None,
|
||||
) -> Optional[
|
||||
Union[
|
||||
OpenAI,
|
||||
AsyncOpenAI,
|
||||
AzureOpenAI,
|
||||
AsyncAzureOpenAI,
|
||||
]
|
||||
]:
|
||||
received_args = locals()
|
||||
openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = None
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None
|
||||
if client is None:
|
||||
data = {}
|
||||
for k, v in received_args.items():
|
||||
|
|
@ -48,7 +60,7 @@ class OpenAIFineTuningAPI:
|
|||
async def acreate_fine_tuning_job(
|
||||
self,
|
||||
create_fine_tuning_job_data: dict,
|
||||
openai_client: AsyncOpenAI,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
) -> FineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.create(
|
||||
**create_fine_tuning_job_data
|
||||
|
|
@ -61,12 +73,17 @@ class OpenAIFineTuningAPI:
|
|||
create_fine_tuning_job_data: dict,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[Union[OpenAI, AsyncOpenAI]] = None,
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
) -> Union[FineTuningJob, Coroutine[Any, Any, FineTuningJob]]:
|
||||
openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client(
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
|
|
@ -74,6 +91,7 @@ class OpenAIFineTuningAPI:
|
|||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
|
|
@ -81,7 +99,7 @@ class OpenAIFineTuningAPI:
|
|||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncOpenAI):
|
||||
if not isinstance(openai_client, (AsyncOpenAI, AsyncAzureOpenAI)):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
|
|
@ -98,7 +116,7 @@ class OpenAIFineTuningAPI:
|
|||
async def acancel_fine_tuning_job(
|
||||
self,
|
||||
fine_tuning_job_id: str,
|
||||
openai_client: AsyncOpenAI,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
) -> FineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.cancel(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
|
|
@ -111,12 +129,17 @@ class OpenAIFineTuningAPI:
|
|||
fine_tuning_job_id: str,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[Union[OpenAI, AsyncOpenAI]] = None,
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
):
|
||||
openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client(
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
|
|
@ -124,6 +147,7 @@ class OpenAIFineTuningAPI:
|
|||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
|
|
@ -131,7 +155,7 @@ class OpenAIFineTuningAPI:
|
|||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncOpenAI):
|
||||
if not isinstance(openai_client, (AsyncOpenAI, AsyncAzureOpenAI)):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
|
|
@ -147,7 +171,7 @@ class OpenAIFineTuningAPI:
|
|||
|
||||
async def alist_fine_tuning_jobs(
|
||||
self,
|
||||
openai_client: AsyncOpenAI,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
):
|
||||
|
|
@ -159,14 +183,19 @@ class OpenAIFineTuningAPI:
|
|||
_is_async: bool,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[Union[OpenAI, AsyncOpenAI]] = None,
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
after: Optional[str] = None,
|
||||
limit: Optional[int] = None,
|
||||
):
|
||||
openai_client: Optional[Union[OpenAI, AsyncOpenAI]] = self.get_openai_client(
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
|
|
@ -174,6 +203,7 @@ class OpenAIFineTuningAPI:
|
|||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
|
|
@ -181,7 +211,7 @@ class OpenAIFineTuningAPI:
|
|||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncOpenAI):
|
||||
if not isinstance(openai_client, (AsyncOpenAI, AsyncAzureOpenAI)):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
|
|
@ -193,4 +223,59 @@ class OpenAIFineTuningAPI:
|
|||
verbose_logger.debug("list fine tuning job, after= %s, limit= %s", after, limit)
|
||||
response = openai_client.fine_tuning.jobs.list(after=after, limit=limit) # type: ignore
|
||||
return response
|
||||
pass
|
||||
|
||||
async def aretrieve_fine_tuning_job(
|
||||
self,
|
||||
fine_tuning_job_id: str,
|
||||
openai_client: Union[AsyncOpenAI, AsyncAzureOpenAI],
|
||||
) -> FineTuningJob:
|
||||
response = await openai_client.fine_tuning.jobs.retrieve(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return response
|
||||
|
||||
def retrieve_fine_tuning_job(
|
||||
self,
|
||||
_is_async: bool,
|
||||
fine_tuning_job_id: str,
|
||||
api_key: Optional[str],
|
||||
api_base: Optional[str],
|
||||
api_version: Optional[str],
|
||||
timeout: Union[float, httpx.Timeout],
|
||||
max_retries: Optional[int],
|
||||
organization: Optional[str],
|
||||
client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = None,
|
||||
):
|
||||
openai_client: Optional[
|
||||
Union[OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI]
|
||||
] = self.get_openai_client(
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
organization=organization,
|
||||
client=client,
|
||||
_is_async=_is_async,
|
||||
api_version=api_version,
|
||||
)
|
||||
if openai_client is None:
|
||||
raise ValueError(
|
||||
"OpenAI client is not initialized. Make sure api_key is passed or OPENAI_API_KEY is set in the environment."
|
||||
)
|
||||
|
||||
if _is_async is True:
|
||||
if not isinstance(openai_client, AsyncOpenAI):
|
||||
raise ValueError(
|
||||
"OpenAI client is not an instance of AsyncOpenAI. Make sure you passed an AsyncOpenAI client."
|
||||
)
|
||||
return self.aretrieve_fine_tuning_job( # type: ignore
|
||||
fine_tuning_job_id=fine_tuning_job_id,
|
||||
openai_client=openai_client,
|
||||
)
|
||||
verbose_logger.debug("retrieving fine tuning job, id= %s", fine_tuning_job_id)
|
||||
response = openai_client.fine_tuning.jobs.retrieve(
|
||||
fine_tuning_job_id=fine_tuning_job_id
|
||||
)
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import hashlib
|
||||
import os
|
||||
import types
|
||||
from typing import (
|
||||
Any,
|
||||
|
|
@ -1306,94 +1305,6 @@ class OpenAIChatCompletion(BaseLLM):
|
|||
|
||||
return HttpxBinaryResponseContent(response=response.response)
|
||||
|
||||
async def ahealth_check(
|
||||
self,
|
||||
model: Optional[str],
|
||||
api_key: Optional[str],
|
||||
timeout: float,
|
||||
mode: str,
|
||||
messages: Optional[list] = None,
|
||||
input: Optional[list] = None,
|
||||
prompt: Optional[str] = None,
|
||||
organization: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
):
|
||||
client = AsyncOpenAI(
|
||||
api_key=api_key,
|
||||
timeout=timeout,
|
||||
organization=organization,
|
||||
base_url=api_base,
|
||||
)
|
||||
if model is None and mode != "image_generation":
|
||||
raise Exception("model is not set")
|
||||
|
||||
completion = None
|
||||
|
||||
if mode == "completion":
|
||||
completion = await client.completions.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "chat":
|
||||
if messages is None:
|
||||
raise Exception("messages is not set")
|
||||
completion = await client.chat.completions.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
messages=messages, # type: ignore
|
||||
)
|
||||
elif mode == "embedding":
|
||||
if input is None:
|
||||
raise Exception("input is not set")
|
||||
completion = await client.embeddings.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
input=input, # type: ignore
|
||||
)
|
||||
elif mode == "image_generation":
|
||||
if prompt is None:
|
||||
raise Exception("prompt is not set")
|
||||
completion = await client.images.with_raw_response.generate(
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "audio_transcription":
|
||||
# Get the current directory of the file being run
|
||||
pwd = os.path.dirname(os.path.realpath(__file__))
|
||||
file_path = os.path.join(
|
||||
pwd, "../../../tests/gettysburg.wav"
|
||||
) # proxy address
|
||||
audio_file = open(file_path, "rb")
|
||||
completion = await client.audio.transcriptions.with_raw_response.create(
|
||||
file=audio_file,
|
||||
model=model, # type: ignore
|
||||
prompt=prompt, # type: ignore
|
||||
)
|
||||
elif mode == "audio_speech":
|
||||
# Get the current directory of the file being run
|
||||
completion = await client.audio.speech.with_raw_response.create(
|
||||
model=model, # type: ignore
|
||||
input=prompt, # type: ignore
|
||||
voice="alloy",
|
||||
)
|
||||
else:
|
||||
raise ValueError("mode not set, passed in mode: " + mode)
|
||||
response = {}
|
||||
|
||||
if completion is None or not hasattr(completion, "headers"):
|
||||
raise Exception("invalid completion response")
|
||||
|
||||
if (
|
||||
completion.headers.get("x-ratelimit-remaining-requests", None) is not None
|
||||
): # not provided for dall-e requests
|
||||
response["x-ratelimit-remaining-requests"] = completion.headers[
|
||||
"x-ratelimit-remaining-requests"
|
||||
]
|
||||
|
||||
if completion.headers.get("x-ratelimit-remaining-tokens", None) is not None:
|
||||
response["x-ratelimit-remaining-tokens"] = completion.headers[
|
||||
"x-ratelimit-remaining-tokens"
|
||||
]
|
||||
return response
|
||||
|
||||
|
||||
class OpenAIFilesAPI(BaseLLM):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -380,6 +380,8 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
if param == "seed":
|
||||
optional_params["seed"] = value
|
||||
|
||||
if litellm.vertex_ai_safety_settings is not None:
|
||||
optional_params["safety_settings"] = litellm.vertex_ai_safety_settings
|
||||
return optional_params
|
||||
|
||||
def get_mapped_special_auth_params(self) -> dict:
|
||||
|
|
|
|||
279
litellm/main.py
279
litellm/main.py
|
|
@ -51,6 +51,11 @@ from litellm import ( # type: ignore
|
|||
get_optional_params,
|
||||
)
|
||||
from litellm.integrations.custom_logger import CustomLogger
|
||||
from litellm.litellm_core_utils.audio_utils.utils import get_audio_file_for_health_check
|
||||
from litellm.litellm_core_utils.health_check_utils import (
|
||||
_create_health_check_response,
|
||||
_filter_model_params,
|
||||
)
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.litellm_core_utils.mock_functions import (
|
||||
mock_embedding,
|
||||
|
|
@ -60,6 +65,7 @@ from litellm.litellm_core_utils.prompt_templates.common_utils import (
|
|||
get_content_from_model_response,
|
||||
)
|
||||
from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler
|
||||
from litellm.realtime_api.main import _realtime_health_check
|
||||
from litellm.secret_managers.main import get_secret_str
|
||||
from litellm.utils import (
|
||||
CustomStreamWrapper,
|
||||
|
|
@ -550,6 +556,17 @@ def _handle_mock_potential_exceptions(
|
|||
), # type: ignore
|
||||
model=model,
|
||||
)
|
||||
elif (
|
||||
isinstance(mock_response, str)
|
||||
and mock_response == "litellm.ContextWindowExceededError"
|
||||
):
|
||||
raise litellm.ContextWindowExceededError(
|
||||
message="this is a mock context window exceeded error",
|
||||
llm_provider=getattr(
|
||||
mock_response, "llm_provider", custom_llm_provider or "openai"
|
||||
), # type: ignore
|
||||
model=model,
|
||||
)
|
||||
elif (
|
||||
isinstance(mock_response, str)
|
||||
and mock_response == "litellm.InternalServerError"
|
||||
|
|
@ -734,7 +751,7 @@ def mock_completion(
|
|||
except Exception as e:
|
||||
if isinstance(e, openai.APIError):
|
||||
raise e
|
||||
raise Exception("Mock completion response failed")
|
||||
raise Exception("Mock completion response failed - {}".format(e))
|
||||
|
||||
|
||||
@client
|
||||
|
|
@ -882,6 +899,10 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
hf_model_name = kwargs.get("hf_model_name", None)
|
||||
supports_system_message = kwargs.get("supports_system_message", None)
|
||||
base_model = kwargs.get("base_model", None)
|
||||
### DISABLE FLAGS ###
|
||||
disable_add_transform_inline_image_block = kwargs.get(
|
||||
"disable_add_transform_inline_image_block", None
|
||||
)
|
||||
### TEXT COMPLETION CALLS ###
|
||||
text_completion = kwargs.get("text_completion", False)
|
||||
atext_completion = kwargs.get("atext_completion", False)
|
||||
|
|
@ -939,14 +960,11 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
"top_logprobs",
|
||||
"extra_headers",
|
||||
]
|
||||
|
||||
default_params = openai_params + all_litellm_params
|
||||
|
||||
litellm_params = {} # used to prevent unbound var errors
|
||||
non_default_params = {
|
||||
k: v for k, v in kwargs.items() if k not in default_params
|
||||
} # model-specific params - pass them straight to the model/provider
|
||||
|
||||
## PROMPT MANAGEMENT HOOKS ##
|
||||
|
||||
if isinstance(litellm_logging_obj, LiteLLMLoggingObj) and prompt_id is not None:
|
||||
|
|
@ -1139,6 +1157,7 @@ def completion( # type: ignore # noqa: PLR0915
|
|||
hf_model_name=hf_model_name,
|
||||
custom_prompt_dict=custom_prompt_dict,
|
||||
litellm_metadata=kwargs.get("litellm_metadata"),
|
||||
disable_add_transform_inline_image_block=disable_add_transform_inline_image_block,
|
||||
)
|
||||
logging.update_environment_variables(
|
||||
model=model,
|
||||
|
|
@ -4850,7 +4869,30 @@ def transcription(
|
|||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
)
|
||||
|
||||
elif custom_llm_provider == "deepgram":
|
||||
response = base_llm_http_handler.audio_transcriptions(
|
||||
model=model,
|
||||
audio_file=file,
|
||||
optional_params=optional_params,
|
||||
model_response=model_response,
|
||||
atranscription=atranscription,
|
||||
client=(
|
||||
client
|
||||
if client is not None
|
||||
and (
|
||||
isinstance(client, HTTPHandler)
|
||||
or isinstance(client, AsyncHTTPHandler)
|
||||
)
|
||||
else None
|
||||
),
|
||||
timeout=timeout,
|
||||
max_retries=max_retries,
|
||||
logging_obj=litellm_logging_obj,
|
||||
api_base=api_base,
|
||||
api_key=api_key,
|
||||
custom_llm_provider="deepgram",
|
||||
headers={},
|
||||
)
|
||||
if response is None:
|
||||
raise ValueError("Unmapped provider passed in. Unable to get the response.")
|
||||
return response
|
||||
|
|
@ -5106,59 +5148,60 @@ def speech(
|
|||
##### Health Endpoints #######################
|
||||
|
||||
|
||||
async def ahealth_check_chat_models(
|
||||
async def ahealth_check_wildcard_models(
|
||||
model: str, custom_llm_provider: str, model_params: dict
|
||||
) -> dict:
|
||||
if "*" in model:
|
||||
from litellm.litellm_core_utils.llm_request_utils import (
|
||||
pick_cheapest_chat_model_from_llm_provider,
|
||||
)
|
||||
|
||||
# this is a wildcard model, we need to pick a random model from the provider
|
||||
cheapest_model = pick_cheapest_chat_model_from_llm_provider(
|
||||
custom_llm_provider=custom_llm_provider
|
||||
)
|
||||
fallback_models: Optional[List] = None
|
||||
if custom_llm_provider in litellm.models_by_provider:
|
||||
models = litellm.models_by_provider[custom_llm_provider]
|
||||
random.shuffle(models) # Shuffle the models list in place
|
||||
fallback_models = models[
|
||||
:2
|
||||
] # Pick the first 2 models from the shuffled list
|
||||
model_params["model"] = cheapest_model
|
||||
model_params["fallbacks"] = fallback_models
|
||||
model_params["max_tokens"] = 1
|
||||
await acompletion(**model_params)
|
||||
response: dict = {} # args like remaining ratelimit etc.
|
||||
else: # default to completion calls
|
||||
model_params["max_tokens"] = 1
|
||||
await acompletion(**model_params)
|
||||
response = {} # args like remaining ratelimit etc.
|
||||
from litellm.litellm_core_utils.llm_request_utils import (
|
||||
pick_cheapest_chat_model_from_llm_provider,
|
||||
)
|
||||
|
||||
# this is a wildcard model, we need to pick a random model from the provider
|
||||
cheapest_model = pick_cheapest_chat_model_from_llm_provider(
|
||||
custom_llm_provider=custom_llm_provider
|
||||
)
|
||||
fallback_models: Optional[List] = None
|
||||
if custom_llm_provider in litellm.models_by_provider:
|
||||
models = litellm.models_by_provider[custom_llm_provider]
|
||||
random.shuffle(models) # Shuffle the models list in place
|
||||
fallback_models = models[:2] # Pick the first 2 models from the shuffled list
|
||||
model_params["model"] = cheapest_model
|
||||
model_params["fallbacks"] = fallback_models
|
||||
model_params["max_tokens"] = 1
|
||||
await acompletion(**model_params)
|
||||
response: dict = {} # args like remaining ratelimit etc.
|
||||
return response
|
||||
|
||||
|
||||
async def ahealth_check( # noqa: PLR0915
|
||||
async def ahealth_check(
|
||||
model_params: dict,
|
||||
mode: Optional[
|
||||
Literal[
|
||||
"completion", "embedding", "image_generation", "chat", "batch", "rerank"
|
||||
"chat",
|
||||
"completion",
|
||||
"embedding",
|
||||
"audio_speech",
|
||||
"audio_transcription",
|
||||
"image_generation",
|
||||
"batch",
|
||||
"rerank",
|
||||
"realtime",
|
||||
]
|
||||
] = None,
|
||||
] = "chat",
|
||||
prompt: Optional[str] = None,
|
||||
input: Optional[List] = None,
|
||||
default_timeout: float = 6000,
|
||||
):
|
||||
"""
|
||||
Support health checks for different providers. Return remaining rate limit, etc.
|
||||
|
||||
For azure/openai -> completion.with_raw_response
|
||||
For rest -> litellm.acompletion()
|
||||
Returns:
|
||||
{
|
||||
"x-ratelimit-remaining-requests": int,
|
||||
"x-ratelimit-remaining-tokens": int,
|
||||
"x-ms-region": str,
|
||||
}
|
||||
"""
|
||||
passed_in_mode: Optional[str] = None
|
||||
try:
|
||||
model: Optional[str] = model_params.get("model", None)
|
||||
|
||||
if model is None:
|
||||
raise Exception("model not set")
|
||||
|
||||
|
|
@ -5166,122 +5209,74 @@ async def ahealth_check( # noqa: PLR0915
|
|||
mode = litellm.model_cost[model].get("mode")
|
||||
|
||||
model, custom_llm_provider, _, _ = get_llm_provider(model=model)
|
||||
|
||||
if model in litellm.model_cost and mode is None:
|
||||
mode = litellm.model_cost[model].get("mode")
|
||||
|
||||
mode = mode
|
||||
passed_in_mode = mode
|
||||
if mode is None:
|
||||
mode = "chat" # default to chat completion calls
|
||||
|
||||
if custom_llm_provider == "azure":
|
||||
api_key = (
|
||||
model_params.get("api_key")
|
||||
or get_secret_str("AZURE_API_KEY")
|
||||
or get_secret_str("AZURE_OPENAI_API_KEY")
|
||||
)
|
||||
|
||||
api_base: Optional[str] = (
|
||||
model_params.get("api_base")
|
||||
or get_secret_str("AZURE_API_BASE")
|
||||
or get_secret_str("AZURE_OPENAI_API_BASE")
|
||||
)
|
||||
|
||||
if api_base is None:
|
||||
raise ValueError(
|
||||
"Azure API Base cannot be None. Set via 'AZURE_API_BASE' in env var or `.completion(..., api_base=..)`"
|
||||
)
|
||||
|
||||
api_version = (
|
||||
model_params.get("api_version")
|
||||
or get_secret_str("AZURE_API_VERSION")
|
||||
or get_secret_str("AZURE_OPENAI_API_VERSION")
|
||||
)
|
||||
|
||||
timeout = (
|
||||
model_params.get("timeout")
|
||||
or litellm.request_timeout
|
||||
or default_timeout
|
||||
)
|
||||
|
||||
response = await azure_chat_completions.ahealth_check(
|
||||
model_params["cache"] = {
|
||||
"no-cache": True
|
||||
} # don't used cached responses for making health check calls
|
||||
mode = mode or "chat"
|
||||
if "*" in model:
|
||||
return await ahealth_check_wildcard_models(
|
||||
model=model,
|
||||
messages=model_params.get(
|
||||
"messages", None
|
||||
), # Replace with your actual messages list
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
api_version=api_version,
|
||||
timeout=timeout,
|
||||
mode=mode,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
model_params=model_params,
|
||||
)
|
||||
# Map modes to their corresponding health check calls
|
||||
mode_handlers = {
|
||||
"chat": lambda: litellm.acompletion(**model_params),
|
||||
"completion": lambda: litellm.atext_completion(
|
||||
**_filter_model_params(model_params),
|
||||
prompt=prompt or "test",
|
||||
),
|
||||
"embedding": lambda: litellm.aembedding(
|
||||
**_filter_model_params(model_params),
|
||||
input=input or ["test"],
|
||||
),
|
||||
"audio_speech": lambda: litellm.aspeech(
|
||||
**_filter_model_params(model_params),
|
||||
input=prompt or "test",
|
||||
voice="alloy",
|
||||
),
|
||||
"audio_transcription": lambda: litellm.atranscription(
|
||||
**_filter_model_params(model_params),
|
||||
file=get_audio_file_for_health_check(),
|
||||
),
|
||||
"image_generation": lambda: litellm.aimage_generation(
|
||||
**_filter_model_params(model_params),
|
||||
prompt=prompt,
|
||||
input=input,
|
||||
)
|
||||
elif (
|
||||
custom_llm_provider == "openai"
|
||||
or custom_llm_provider == "text-completion-openai"
|
||||
):
|
||||
api_key = model_params.get("api_key") or get_secret_str("OPENAI_API_KEY")
|
||||
organization = model_params.get("organization")
|
||||
|
||||
timeout = (
|
||||
model_params.get("timeout")
|
||||
or litellm.request_timeout
|
||||
or default_timeout
|
||||
)
|
||||
|
||||
api_base = model_params.get("api_base") or get_secret_str("OPENAI_API_BASE")
|
||||
|
||||
if custom_llm_provider == "text-completion-openai":
|
||||
mode = "completion"
|
||||
|
||||
response = await openai_chat_completions.ahealth_check(
|
||||
),
|
||||
"rerank": lambda: litellm.arerank(
|
||||
**_filter_model_params(model_params),
|
||||
query=prompt or "",
|
||||
documents=["my sample text"],
|
||||
),
|
||||
"realtime": lambda: _realtime_health_check(
|
||||
model=model,
|
||||
messages=model_params.get(
|
||||
"messages", None
|
||||
), # Replace with your actual messages list
|
||||
api_key=api_key,
|
||||
api_base=api_base,
|
||||
timeout=timeout,
|
||||
mode=mode,
|
||||
prompt=prompt,
|
||||
input=input,
|
||||
organization=organization,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
api_base=model_params.get("api_base", None),
|
||||
api_key=model_params.get("api_key", None),
|
||||
api_version=model_params.get("api_version", None),
|
||||
),
|
||||
}
|
||||
|
||||
if mode in mode_handlers:
|
||||
_response = await mode_handlers[mode]()
|
||||
# Only process headers for chat mode
|
||||
_response_headers: dict = (
|
||||
getattr(_response, "_hidden_params", {}).get("headers", {}) or {}
|
||||
)
|
||||
return _create_health_check_response(_response_headers)
|
||||
else:
|
||||
model_params["cache"] = {
|
||||
"no-cache": True
|
||||
} # don't used cached responses for making health check calls
|
||||
if mode == "embedding":
|
||||
model_params.pop("messages", None)
|
||||
model_params["input"] = input
|
||||
await litellm.aembedding(**model_params)
|
||||
response = {}
|
||||
elif mode == "image_generation":
|
||||
model_params.pop("messages", None)
|
||||
model_params["prompt"] = prompt
|
||||
await litellm.aimage_generation(**model_params)
|
||||
response = {}
|
||||
elif mode == "rerank":
|
||||
model_params.pop("messages", None)
|
||||
model_params["query"] = prompt
|
||||
model_params["documents"] = ["my sample text"]
|
||||
await litellm.arerank(**model_params)
|
||||
response = {}
|
||||
else:
|
||||
response = await ahealth_check_chat_models(
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
model_params=model_params,
|
||||
)
|
||||
return response
|
||||
raise Exception(
|
||||
f"Mode {mode} not supported. See modes here: https://docs.litellm.ai/docs/proxy/health"
|
||||
)
|
||||
except Exception as e:
|
||||
stack_trace = traceback.format_exc()
|
||||
if isinstance(stack_trace, str):
|
||||
stack_trace = stack_trace[:1000]
|
||||
|
||||
if passed_in_mode is None:
|
||||
if mode is None:
|
||||
return {
|
||||
"error": f"error:{str(e)}. Missing `mode`. Set the `mode` for the model - https://docs.litellm.ai/docs/proxy/health#embedding-models \nstacktrace: {stack_trace}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3480,8 +3480,28 @@
|
|||
"max_tokens": 8191,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8191,
|
||||
"input_cost_per_token": 0.000003,
|
||||
"output_cost_per_token": 0.000009,
|
||||
"input_cost_per_token": 0.000002,
|
||||
"output_cost_per_token": 0.000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
},
|
||||
"vertex_ai/mistral-large@2411-001": {
|
||||
"max_tokens": 8191,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8191,
|
||||
"input_cost_per_token": 0.000002,
|
||||
"output_cost_per_token": 0.000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
},
|
||||
"vertex_ai/mistral-large-2411": {
|
||||
"max_tokens": 8191,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8191,
|
||||
"input_cost_per_token": 0.000002,
|
||||
"output_cost_per_token": 0.000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
|
|
@ -3490,8 +3510,8 @@
|
|||
"max_tokens": 8191,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8191,
|
||||
"input_cost_per_token": 0.000003,
|
||||
"output_cost_per_token": 0.000009,
|
||||
"input_cost_per_token": 0.000002,
|
||||
"output_cost_per_token": 0.000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
|
|
@ -3500,8 +3520,8 @@
|
|||
"max_tokens": 128000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 128000,
|
||||
"input_cost_per_token": 0.000003,
|
||||
"output_cost_per_token": 0.000003,
|
||||
"input_cost_per_token": 0.00000015,
|
||||
"output_cost_per_token": 0.00000015,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
|
|
@ -3565,8 +3585,8 @@
|
|||
"max_tokens": 128000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 128000,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000003,
|
||||
"input_cost_per_token": 0.0000002,
|
||||
"output_cost_per_token": 0.0000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
|
|
@ -3575,8 +3595,8 @@
|
|||
"max_tokens": 128000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 128000,
|
||||
"input_cost_per_token": 0.000001,
|
||||
"output_cost_per_token": 0.000003,
|
||||
"input_cost_per_token": 0.0000002,
|
||||
"output_cost_per_token": 0.0000006,
|
||||
"litellm_provider": "vertex_ai-mistral_models",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true
|
||||
|
|
@ -4487,13 +4507,24 @@
|
|||
"litellm_provider": "replicate",
|
||||
"mode": "chat"
|
||||
},
|
||||
"openrouter/deepseek/deepseek-coder": {
|
||||
"max_tokens": 4096,
|
||||
"max_input_tokens": 32000,
|
||||
"openrouter/deepseek/deepseek-chat": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 66000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.00000014,
|
||||
"output_cost_per_token": 0.00000028,
|
||||
"litellm_provider": "openrouter",
|
||||
"supports_prompt_caching": true,
|
||||
"mode": "chat"
|
||||
},
|
||||
"openrouter/deepseek/deepseek-coder": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 66000,
|
||||
"max_output_tokens": 4096,
|
||||
"input_cost_per_token": 0.00000014,
|
||||
"output_cost_per_token": 0.00000028,
|
||||
"litellm_provider": "openrouter",
|
||||
"supports_prompt_caching": true,
|
||||
"mode": "chat"
|
||||
},
|
||||
"openrouter/microsoft/wizardlm-2-8x22b:nitro": {
|
||||
|
|
@ -4671,6 +4702,22 @@
|
|||
"litellm_provider": "openrouter",
|
||||
"mode": "chat"
|
||||
},
|
||||
"openrouter/openai/o1": {
|
||||
"max_tokens": 100000,
|
||||
"max_input_tokens": 200000,
|
||||
"max_output_tokens": 100000,
|
||||
"input_cost_per_token": 0.000015,
|
||||
"output_cost_per_token": 0.00006,
|
||||
"cache_read_input_token_cost": 0.0000075,
|
||||
"litellm_provider": "openrouter",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_parallel_function_calling": true,
|
||||
"supports_vision": true,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_response_schema": true
|
||||
},
|
||||
"openrouter/openai/o1-mini": {
|
||||
"max_tokens": 65536,
|
||||
"max_input_tokens": 128000,
|
||||
|
|
|
|||
1
litellm/proxy/_experimental/out/404.html
Normal file
1
litellm/proxy/_experimental/out/404.html
Normal file
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 @@
|
|||
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[665],{23152:function(e,t,s){s.d(t,{GH$:function(){return l}});var c=s(2265);let l=({color:e="currentColor",size:t=24,className:s,...l})=>c.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",width:t,height:t,fill:e,...l,className:"remixicon "+(s||"")},c.createElement("path",{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11.0026 16L6.75999 11.7574L8.17421 10.3431L11.0026 13.1716L16.6595 7.51472L18.0737 8.92893L11.0026 16Z"}))}}]);
|
||||
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[665],{84566:function(e,t,s){s.d(t,{GH$:function(){return l}});var c=s(2265);let l=({color:e="currentColor",size:t=24,className:s,...l})=>c.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",width:t,height:t,fill:e,...l,className:"remixicon "+(s||"")},c.createElement("path",{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11.0026 16L6.75999 11.7574L8.17421 10.3431L11.0026 13.1716L16.6595 7.51472L18.0737 8.92893L11.0026 16Z"}))}}]);
|
||||
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([[409],{67589:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return n(35457)}])},35457:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}}),n(99920);let i=n(57437);n(2265);let o={fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},l={display:"inline-block"},r={display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},d={fontSize:14,fontWeight:400,lineHeight:"49px",margin:0};function s(){return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("title",{children:"404: This page could not be found."}),(0,i.jsx)("div",{style:o,children:(0,i.jsxs)("div",{children:[(0,i.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,i.jsx)("h1",{className:"next-error-h1",style:r,children:"404"}),(0,i.jsx)("div",{style:l,children:(0,i.jsx)("h2",{style:d,children:"This page could not be found."})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},function(e){e.O(0,[971,23,744],function(){return e(e.s=67589)}),_N_E=e.O()}]);
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[409],{67589:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_not-found/page",function(){return n(83634)}])},83634:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"default",{enumerable:!0,get:function(){return s}}),n(47043);let i=n(57437);n(2265);let o={fontFamily:'system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"',height:"100vh",textAlign:"center",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center"},l={display:"inline-block"},r={display:"inline-block",margin:"0 20px 0 0",padding:"0 23px 0 0",fontSize:24,fontWeight:500,verticalAlign:"top",lineHeight:"49px"},d={fontSize:14,fontWeight:400,lineHeight:"49px",margin:0};function s(){return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)("title",{children:"404: This page could not be found."}),(0,i.jsx)("div",{style:o,children:(0,i.jsxs)("div",{children:[(0,i.jsx)("style",{dangerouslySetInnerHTML:{__html:"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}),(0,i.jsx)("h1",{className:"next-error-h1",style:r,children:"404"}),(0,i.jsx)("div",{style:l,children:(0,i.jsx)("h2",{style:d,children:"This page could not be found."})})]})})]})}("function"==typeof t.default||"object"==typeof t.default&&null!==t.default)&&void 0===t.default.__esModule&&(Object.defineProperty(t.default,"__esModule",{value:!0}),Object.assign(t.default,t),e.exports=t.default)}},function(e){e.O(0,[971,117,744],function(){return e(e.s=67589)}),_N_E=e.O()}]);
|
||||
|
|
@ -1 +0,0 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{6580:function(n,e,t){Promise.resolve().then(t.t.bind(t,64671,23)),Promise.resolve().then(t.t.bind(t,53054,23))},53054:function(){},64671:function(n){n.exports={style:{fontFamily:"'__Inter_cf7686', '__Inter_Fallback_cf7686'",fontStyle:"normal"},className:"__className_cf7686"}}},function(n){n.O(0,[959,141,971,23,744],function(){return n(n.s=6580)}),_N_E=n.O()}]);
|
||||
|
|
@ -0,0 +1 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[185],{6580:function(n,e,t){Promise.resolve().then(t.t.bind(t,39974,23)),Promise.resolve().then(t.t.bind(t,2778,23))},2778:function(){},39974:function(n){n.exports={style:{fontFamily:"'__Inter_cf7686', '__Inter_Fallback_cf7686'",fontStyle:"normal"},className:"__className_cf7686"}}},function(n){n.O(0,[919,986,971,117,744],function(){return n(n.s=6580)}),_N_E=n.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([[461],{32922:function(e,s,t){Promise.resolve().then(t.bind(t,62105))},62105:function(e,s,t){"use strict";t.r(s),t.d(s,{default:function(){return g}});var l=t(57437),n=t(2265),a=t(16463),i=t(15606),r=t(85272),o=t(75203),c=t(56745),u=t(46430),d=t(29312),m=t(50586),h=t(99295),x=t(23152),f=t(38939),p=t(49714),j=t(26267),_=t(46682);function g(){let[e]=j.Z.useForm(),s=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let s=document.cookie.split("; ").find(s=>s.startsWith(e+"="));s&&s.split("=")[1]}("token");let t=s.get("invitation_id"),[g,Z]=(0,n.useState)(null),[k,w]=(0,n.useState)(""),[S,b]=(0,n.useState)(""),[N,v]=(0,n.useState)(null),[y,E]=(0,n.useState)(""),[I,O]=(0,n.useState)("");return(0,n.useEffect)(()=>{t&&(0,f.W_)(t).then(e=>{let s=e.login_url;console.log("login_url:",s),E(s);let t=e.token,l=(0,p.o)(t);O(t),console.log("decoded:",l),Z(l.key),console.log("decoded user email:",l.user_email),b(l.user_email),v(l.user_id)})},[t]),(0,l.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,l.jsxs)(o.Z,{children:[(0,l.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,l.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,l.jsx)(d.Z,{children:"Claim your user account to login to Admin UI."}),(0,l.jsx)(r.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,l.jsxs)(u.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,l.jsx)(c.Z,{children:"SSO is under the Enterprise Tirer."}),(0,l.jsx)(c.Z,{children:(0,l.jsx)(i.Z,{variant:"primary",className:"mb-2",children:(0,l.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,l.jsxs)(j.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",g,"token:",I,"formValues:",e),g&&I&&(e.user_email=S,N&&t&&(0,f.m_)(g,t,N,e.password).then(e=>{var s;let t="/ui/";t+="?userID="+((null===(s=e.data)||void 0===s?void 0:s.user_id)||e.user_id),document.cookie="token="+I,console.log("redirecting to:",t),window.location.href=t}))},children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(j.Z.Item,{label:"Email Address",name:"user_email",children:(0,l.jsx)(m.Z,{type:"email",disabled:!0,value:S,defaultValue:S,className:"max-w-md"})}),(0,l.jsx)(j.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,l.jsx)(m.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,l.jsx)("div",{className:"mt-10",children:(0,l.jsx)(_.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}}},function(e){e.O(0,[665,569,748,939,971,23,744],function(){return e(e.s=32922)}),_N_E=e.O()}]);
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[461],{32922: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 g}});var l=t(57437),n=t(2265),a=t(99376),i=t(20831),r=t(94789),o=t(12514),c=t(49804),u=t(67101),d=t(84264),m=t(26637),h=t(96761),x=t(84566),f=t(19250),p=t(14474),j=t(13634),_=t(73002);function g(){let[e]=j.Z.useForm(),s=(0,a.useSearchParams)();!function(e){console.log("COOKIES",document.cookie);let s=document.cookie.split("; ").find(s=>s.startsWith(e+"="));s&&s.split("=")[1]}("token");let t=s.get("invitation_id"),[g,Z]=(0,n.useState)(null),[k,w]=(0,n.useState)(""),[S,b]=(0,n.useState)(""),[N,v]=(0,n.useState)(null),[y,E]=(0,n.useState)(""),[I,O]=(0,n.useState)("");return(0,n.useEffect)(()=>{t&&(0,f.W_)(t).then(e=>{let s=e.login_url;console.log("login_url:",s),E(s);let t=e.token,l=(0,p.o)(t);O(t),console.log("decoded:",l),Z(l.key),console.log("decoded user email:",l.user_email),b(l.user_email),v(l.user_id)})},[t]),(0,l.jsx)("div",{className:"mx-auto w-full max-w-md mt-10",children:(0,l.jsxs)(o.Z,{children:[(0,l.jsx)(h.Z,{className:"text-sm mb-5 text-center",children:"\uD83D\uDE85 LiteLLM"}),(0,l.jsx)(h.Z,{className:"text-xl",children:"Sign up"}),(0,l.jsx)(d.Z,{children:"Claim your user account to login to Admin UI."}),(0,l.jsx)(r.Z,{className:"mt-4",title:"SSO",icon:x.GH$,color:"sky",children:(0,l.jsxs)(u.Z,{numItems:2,className:"flex justify-between items-center",children:[(0,l.jsx)(c.Z,{children:"SSO is under the Enterprise Tirer."}),(0,l.jsx)(c.Z,{children:(0,l.jsx)(i.Z,{variant:"primary",className:"mb-2",children:(0,l.jsx)("a",{href:"https://forms.gle/W3U4PZpJGFHWtHyA9",target:"_blank",children:"Get Free Trial"})})})]})}),(0,l.jsxs)(j.Z,{className:"mt-10 mb-5 mx-auto",layout:"vertical",onFinish:e=>{console.log("in handle submit. accessToken:",g,"token:",I,"formValues:",e),g&&I&&(e.user_email=S,N&&t&&(0,f.m_)(g,t,N,e.password).then(e=>{var s;let t="/ui/";t+="?userID="+((null===(s=e.data)||void 0===s?void 0:s.user_id)||e.user_id),document.cookie="token="+I,console.log("redirecting to:",t),window.location.href=t}))},children:[(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(j.Z.Item,{label:"Email Address",name:"user_email",children:(0,l.jsx)(m.Z,{type:"email",disabled:!0,value:S,defaultValue:S,className:"max-w-md"})}),(0,l.jsx)(j.Z.Item,{label:"Password",name:"password",rules:[{required:!0,message:"password required to sign up"}],help:"Create a password for your account",children:(0,l.jsx)(m.Z,{placeholder:"",type:"password",className:"max-w-md"})})]}),(0,l.jsx)("div",{className:"mt-10",children:(0,l.jsx)(_.ZP,{htmlType:"submit",children:"Sign Up"})})]})]})})}}},function(e){e.O(0,[665,42,755,250,971,117,744],function(){return e(e.s=32922)}),_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
|
|
@ -0,0 +1 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{20169:function(e,n,t){Promise.resolve().then(t.t.bind(t,12846,23)),Promise.resolve().then(t.t.bind(t,19107,23)),Promise.resolve().then(t.t.bind(t,61060,23)),Promise.resolve().then(t.t.bind(t,4707,23)),Promise.resolve().then(t.t.bind(t,80,23)),Promise.resolve().then(t.t.bind(t,36423,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,117],function(){return n(54278),n(20169)}),_N_E=e.O()}]);
|
||||
|
|
@ -1 +0,0 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[744],{20169:function(e,n,t){Promise.resolve().then(t.t.bind(t,95751,23)),Promise.resolve().then(t.t.bind(t,66513,23)),Promise.resolve().then(t.t.bind(t,76130,23)),Promise.resolve().then(t.t.bind(t,39275,23)),Promise.resolve().then(t.t.bind(t,16585,23)),Promise.resolve().then(t.t.bind(t,61343,23))}},function(e){var n=function(n){return e(e.s=n)};e.O(0,[971,23],function(){return n(11028),n(20169)}),_N_E=e.O()}]);
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{41597:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return u(52239)}])}},function(n){var _=function(_){return n(n.s=_)};n.O(0,[774,179],function(){return _(41597),_(26036)}),_N_E=n.O()}]);
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{41597:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return u(48141)}])}},function(n){var _=function(_){return n(n.s=_)};n.O(0,[774,179],function(){return _(41597),_(37253)}),_N_E=n.O()}]);
|
||||
|
|
@ -1 +1 @@
|
|||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{81981:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return u(83387)}])}},function(n){n.O(0,[888,774,179],function(){return n(n.s=81981)}),_N_E=n.O()}]);
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[820],{81981:function(n,_,u){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_error",function(){return u(18529)}])}},function(n){n.O(0,[888,774,179],function(){return n(n.s=81981)}),_N_E=n.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 @@
|
|||
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={id:e,loaded:!1,exports:{}},r=!0;try{a[e].call(n.exports,n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.loaded=!0,n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/ui/_next/",i={272:0,959:0,141:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(/^(141|272|959)$/.test(e))i[e]=0;else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f))}();
|
||||
!function(){"use strict";var e,t,n,r,o,u,i,c,f,a={},l={};function d(e){var t=l[e];if(void 0!==t)return t.exports;var n=l[e]={id:e,loaded:!1,exports:{}},r=!0;try{a[e].call(n.exports,n,n.exports,d),r=!1}finally{r&&delete l[e]}return n.loaded=!0,n.exports}d.m=a,e=[],d.O=function(t,n,r,o){if(n){o=o||0;for(var u=e.length;u>0&&e[u-1][2]>o;u--)e[u]=e[u-1];e[u]=[n,r,o];return}for(var i=1/0,u=0;u<e.length;u++){for(var n=e[u][0],r=e[u][1],o=e[u][2],c=!0,f=0;f<n.length;f++)i>=o&&Object.keys(d.O).every(function(e){return d.O[e](n[f])})?n.splice(f--,1):(c=!1,o<i&&(i=o));if(c){e.splice(u--,1);var a=r();void 0!==a&&(t=a)}}return t},d.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(t,{a:t}),t},n=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__},d.t=function(e,r){if(1&r&&(e=this(e)),8&r||"object"==typeof e&&e&&(4&r&&e.__esModule||16&r&&"function"==typeof e.then))return e;var o=Object.create(null);d.r(o);var u={};t=t||[null,n({}),n([]),n(n)];for(var i=2&r&&e;"object"==typeof i&&!~t.indexOf(i);i=n(i))Object.getOwnPropertyNames(i).forEach(function(t){u[t]=function(){return e[t]}});return u.default=function(){return e},d.d(o,u),o},d.d=function(e,t){for(var n in t)d.o(t,n)&&!d.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},d.f={},d.e=function(e){return Promise.all(Object.keys(d.f).reduce(function(t,n){return d.f[n](e,t),t},[]))},d.u=function(e){},d.miniCssF=function(e){},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r={},o="_N_E:",d.l=function(e,t,n,u){if(r[e]){r[e].push(t);return}if(void 0!==n)for(var i,c,f=document.getElementsByTagName("script"),a=0;a<f.length;a++){var l=f[a];if(l.getAttribute("src")==e||l.getAttribute("data-webpack")==o+n){i=l;break}}i||(c=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,d.nc&&i.setAttribute("nonce",d.nc),i.setAttribute("data-webpack",o+n),i.src=d.tu(e)),r[e]=[t];var s=function(t,n){i.onerror=i.onload=null,clearTimeout(p);var o=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(function(e){return e(n)}),t)return t(n)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=s.bind(null,i.onerror),i.onload=s.bind(null,i.onload),c&&document.head.appendChild(i)},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.nmd=function(e){return e.paths=[],e.children||(e.children=[]),e},d.tt=function(){return void 0===u&&(u={createScriptURL:function(e){return e}},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(u=trustedTypes.createPolicy("nextjs#bundler",u))),u},d.tu=function(e){return d.tt().createScriptURL(e)},d.p="/ui/_next/",i={272:0,919:0,986:0},d.f.j=function(e,t){var n=d.o(i,e)?i[e]:void 0;if(0!==n){if(n)t.push(n[2]);else if(/^(272|919|986)$/.test(e))i[e]=0;else{var r=new Promise(function(t,r){n=i[e]=[t,r]});t.push(n[2]=r);var o=d.p+d.u(e),u=Error();d.l(o,function(t){if(d.o(i,e)&&(0!==(n=i[e])&&(i[e]=void 0),n)){var r=t&&("load"===t.type?"missing":t.type),o=t&&t.target&&t.target.src;u.message="Loading chunk "+e+" failed.\n("+r+": "+o+")",u.name="ChunkLoadError",u.type=r,u.request=o,n[1](u)}},"chunk-"+e,e)}}},d.O.j=function(e){return 0===i[e]},c=function(e,t){var n,r,o=t[0],u=t[1],c=t[2],f=0;if(o.some(function(e){return 0!==i[e]})){for(n in u)d.o(u,n)&&(d.m[n]=u[n]);if(c)var a=c(d)}for(e&&e(t);f<o.length;f++)r=o[f],d.o(i,r)&&i[r]&&i[r][0](),i[r]=0;return d.O(a)},(f=self.webpackChunk_N_E=self.webpackChunk_N_E||[]).forEach(c.bind(null,0)),f.push=c.bind(null,f.push.bind(f))}();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static/chunks/pages/_error-6ae619510b1539d6.js"],sortedPages:["/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||
|
|
@ -0,0 +1 @@
|
|||
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/_error":["static/chunks/pages/_error-28b803cb2479b966.js"],sortedPages:["/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
||||
|
|
@ -1 +1 @@
|
|||
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-472fd7f7b6423233.js"/><script src="/ui/_next/static/chunks/fd9d1056-14fddc9b0ec5bb3e.js" async=""></script><script src="/ui/_next/static/chunks/23-8f224000d2532648.js" async=""></script><script src="/ui/_next/static/chunks/main-app-a85ad02fb0785c2e.js" async=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-472fd7f7b6423233.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/a34f9d1faa5f3315-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/86f6cc749f6b8493.css\",\"style\"]\n3:HL[\"/ui/_next/static/css/0940e922324b3d86.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"4:I[95751,[],\"\"]\n6:I[66513,[],\"ClientPageRoot\"]\n7:I[79642,[\"665\",\"static/chunks/3014691f-0e3faeb6a30bdef5.js\",\"569\",\"static/chunks/569-fb5d4ecc1efc8220.js\",\"852\",\"static/chunks/852-8504c1dc77126607.js\",\"748\",\"static/chunks/748-ca0d88d4d972a1fe.js\",\"17\",\"static/chunks/17-dde5d33395f2c56a.js\",\"939\",\"static/chunks/939-446113ab9fc7760f.js\",\"931\",\"static/chunks/app/page-584ecd9b521c43ed.js\"],\"default\",1]\n8:I[39275,[],\"\"]\n9:I[61343,[],\"\"]\nb:I[76130,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L4\",null,{\"buildId\":\"eGb6WmzHhBl0gtALxCVS5\",\"assetPrefix\":\"/ui\",\"urlParts\":[\"\",\"\"],\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[[\"$L5\",[\"$\",\"$L6\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$7\"}],null],null],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/86f6cc749f6b8493.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/0940e922324b3d86.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_cf7686\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"LiteLLM Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"LiteLLM Proxy Admin UI\"}],[\"$\",\"link\",\"4\",{\"rel\":\"icon\",\"href\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script></body></html>
|
||||
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/ui/_next/static/chunks/webpack-75a5453f51d60261.js"/><script src="/ui/_next/static/chunks/fd9d1056-896c4ac5495ec10d.js" async=""></script><script src="/ui/_next/static/chunks/117-7e4422a742f8f04c.js" async=""></script><script src="/ui/_next/static/chunks/main-app-475d6efe4080647d.js" async=""></script><title>LiteLLM Dashboard</title><meta name="description" content="LiteLLM Proxy Admin UI"/><link rel="icon" href="/ui/favicon.ico" type="image/x-icon" sizes="16x16"/><meta name="next-size-adjust"/><script src="/ui/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><script src="/ui/_next/static/chunks/webpack-75a5453f51d60261.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/ui/_next/static/media/a34f9d1faa5f3315-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/ui/_next/static/css/86f6cc749f6b8493.css\",\"style\"]\n3:HL[\"/ui/_next/static/css/e1b24213957c55c5.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"4:I[12846,[],\"\"]\n6:I[19107,[],\"ClientPageRoot\"]\n7:I[81739,[\"665\",\"static/chunks/3014691f-879b58d3fc1547e5.js\",\"42\",\"static/chunks/42-4c9a56afdf4d8d5b.js\",\"261\",\"static/chunks/261-1a8c5eb3e86e9745.js\",\"755\",\"static/chunks/755-c7e0ae255f32cb18.js\",\"305\",\"static/chunks/305-ea5b5dacbe65306f.js\",\"250\",\"static/chunks/250-a41de2a0aaa99457.js\",\"931\",\"static/chunks/app/page-1dd1b1160f707c77.js\"],\"default\",1]\n8:I[4707,[],\"\"]\n9:I[36423,[],\"\"]\nb:I[61060,[],\"\"]\nc:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L4\",null,{\"buildId\":\"qvIE1gx5DzMNKdXF58zoV\",\"assetPrefix\":\"/ui\",\"urlParts\":[\"\",\"\"],\"initialTree\":[\"\",{\"children\":[\"__PAGE__\",{}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"__PAGE__\",{},[[\"$L5\",[\"$\",\"$L6\",null,{\"props\":{\"params\":{},\"searchParams\":{}},\"Component\":\"$7\"}],null],null],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/86f6cc749f6b8493.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/ui/_next/static/css/e1b24213957c55c5.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[\"$\",\"body\",null,{\"className\":\"__className_cf7686\",\"children\":[\"$\",\"$L8\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L9\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[null,\"$La\"],\"globalErrorComponent\":\"$b\",\"missingSlots\":\"$Wc\"}]\n"])</script><script>self.__next_f.push([1,"a:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"LiteLLM Dashboard\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"LiteLLM Proxy Admin UI\"}],[\"$\",\"link\",\"4\",{\"rel\":\"icon\",\"href\":\"/ui/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"16x16\"}],[\"$\",\"meta\",\"5\",{\"name\":\"next-size-adjust\"}]]\n5:null\n"])</script></body></html>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
2:I[66513,[],"ClientPageRoot"]
|
||||
3:I[79642,["665","static/chunks/3014691f-0e3faeb6a30bdef5.js","569","static/chunks/569-fb5d4ecc1efc8220.js","852","static/chunks/852-8504c1dc77126607.js","748","static/chunks/748-ca0d88d4d972a1fe.js","17","static/chunks/17-dde5d33395f2c56a.js","939","static/chunks/939-446113ab9fc7760f.js","931","static/chunks/app/page-584ecd9b521c43ed.js"],"default",1]
|
||||
4:I[39275,[],""]
|
||||
5:I[61343,[],""]
|
||||
0:["eGb6WmzHhBl0gtALxCVS5",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/86f6cc749f6b8493.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/ui/_next/static/css/0940e922324b3d86.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_cf7686","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
2:I[19107,[],"ClientPageRoot"]
|
||||
3:I[81739,["665","static/chunks/3014691f-879b58d3fc1547e5.js","42","static/chunks/42-4c9a56afdf4d8d5b.js","261","static/chunks/261-1a8c5eb3e86e9745.js","755","static/chunks/755-c7e0ae255f32cb18.js","305","static/chunks/305-ea5b5dacbe65306f.js","250","static/chunks/250-a41de2a0aaa99457.js","931","static/chunks/app/page-1dd1b1160f707c77.js"],"default",1]
|
||||
4:I[4707,[],""]
|
||||
5:I[36423,[],""]
|
||||
0:["qvIE1gx5DzMNKdXF58zoV",[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/86f6cc749f6b8493.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/ui/_next/static/css/e1b24213957c55c5.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_cf7686","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
||||
1:null
|
||||
|
|
|
|||
1
litellm/proxy/_experimental/out/model_hub.html
Normal file
1
litellm/proxy/_experimental/out/model_hub.html
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
2:I[66513,[],"ClientPageRoot"]
|
||||
3:I[99327,["569","static/chunks/569-fb5d4ecc1efc8220.js","852","static/chunks/852-8504c1dc77126607.js","939","static/chunks/939-446113ab9fc7760f.js","418","static/chunks/app/model_hub/page-544de941cb7f8d17.js"],"default",1]
|
||||
4:I[39275,[],""]
|
||||
5:I[61343,[],""]
|
||||
0:["eGb6WmzHhBl0gtALxCVS5",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/86f6cc749f6b8493.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/ui/_next/static/css/0940e922324b3d86.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_cf7686","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
2:I[19107,[],"ClientPageRoot"]
|
||||
3:I[52829,["42","static/chunks/42-4c9a56afdf4d8d5b.js","261","static/chunks/261-1a8c5eb3e86e9745.js","250","static/chunks/250-a41de2a0aaa99457.js","418","static/chunks/app/model_hub/page-b714c2babfdfc41f.js"],"default",1]
|
||||
4:I[4707,[],""]
|
||||
5:I[36423,[],""]
|
||||
0:["qvIE1gx5DzMNKdXF58zoV",[[["",{"children":["model_hub",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",{"children":["model_hub",{"children":["__PAGE__",{},[["$L1",["$","$L2",null,{"props":{"params":{},"searchParams":{}},"Component":"$3"}],null],null],null]},[null,["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children","model_hub","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]],null]},[[[["$","link","0",{"rel":"stylesheet","href":"/ui/_next/static/css/86f6cc749f6b8493.css","precedence":"next","crossOrigin":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/ui/_next/static/css/e1b24213957c55c5.css","precedence":"next","crossOrigin":"$undefined"}]],["$","html",null,{"lang":"en","children":["$","body",null,{"className":"__className_cf7686","children":["$","$L4",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]],null],null],["$L6",null]]]]
|
||||
6:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","1",{"charSet":"utf-8"}],["$","title","2",{"children":"LiteLLM Dashboard"}],["$","meta","3",{"name":"description","content":"LiteLLM Proxy Admin UI"}],["$","link","4",{"rel":"icon","href":"/ui/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","meta","5",{"name":"next-size-adjust"}]]
|
||||
1:null
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue