Merge branch 'main' into litellm_/audio/speech
|
|
@ -3339,7 +3339,7 @@ jobs:
|
|||
python -m build
|
||||
twine upload --verbose dist/*
|
||||
|
||||
e2e_ui_testing:
|
||||
ui_build:
|
||||
machine:
|
||||
image: ubuntu-2204:2023.10.1
|
||||
resource_class: xlarge
|
||||
|
|
@ -3366,6 +3366,48 @@ jobs:
|
|||
|
||||
# Now source the build script
|
||||
source ./build_ui.sh
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- litellm/proxy/_experimental/out
|
||||
|
||||
ui_unit_tests:
|
||||
machine:
|
||||
image: ubuntu-2204:2023.10.1
|
||||
resource_class: xlarge
|
||||
working_directory: ~/project
|
||||
steps:
|
||||
- checkout
|
||||
- setup_google_dns
|
||||
- run:
|
||||
name: Run UI unit tests (Vitest)
|
||||
command: |
|
||||
# Use Node 20 (several deps require >=20)
|
||||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
source "$NVM_DIR/nvm.sh"
|
||||
nvm install 20
|
||||
nvm use 20
|
||||
|
||||
cd ui/litellm-dashboard
|
||||
npm ci || npm install
|
||||
|
||||
# CI run, with both LCOV (Codecov) and HTML (artifact you can click)
|
||||
CI=true npm run test -- --run --coverage \
|
||||
--coverage.provider=v8 \
|
||||
--coverage.reporter=lcov \
|
||||
--coverage.reporter=html \
|
||||
--coverage.reportsDirectory=coverage/html
|
||||
|
||||
e2e_ui_testing:
|
||||
machine:
|
||||
image: ubuntu-2204:2023.10.1
|
||||
resource_class: xlarge
|
||||
working_directory: ~/project
|
||||
steps:
|
||||
- checkout
|
||||
- setup_google_dns
|
||||
- attach_workspace:
|
||||
at: ~/project
|
||||
- run:
|
||||
name: Upgrade Docker to v24.x (API 1.44+)
|
||||
command: |
|
||||
|
|
@ -3411,24 +3453,6 @@ jobs:
|
|||
name: Install Playwright Browsers
|
||||
command: |
|
||||
npx playwright install
|
||||
- run:
|
||||
name: Run UI unit tests (Vitest)
|
||||
command: |
|
||||
# Use Node 20 (several deps require >=20)
|
||||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
source "$NVM_DIR/nvm.sh"
|
||||
nvm install 20
|
||||
nvm use 20
|
||||
|
||||
cd ui/litellm-dashboard
|
||||
npm ci || npm install
|
||||
|
||||
# CI run, with both LCOV (Codecov) and HTML (artifact you can click)
|
||||
CI=true npm run test -- --run --coverage \
|
||||
--coverage.provider=v8 \
|
||||
--coverage.reporter=lcov \
|
||||
--coverage.reporter=html \
|
||||
--coverage.reportsDirectory=coverage/html
|
||||
|
||||
- run:
|
||||
name: Build Docker image
|
||||
|
|
@ -3633,6 +3657,20 @@ workflows:
|
|||
only:
|
||||
- main
|
||||
- /litellm_.*/
|
||||
- ui_build:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /litellm_.*/
|
||||
- ui_unit_tests:
|
||||
requires:
|
||||
- ui_build
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- /litellm_.*/
|
||||
- auth_ui_unit_tests:
|
||||
filters:
|
||||
branches:
|
||||
|
|
@ -3640,6 +3678,8 @@ workflows:
|
|||
- main
|
||||
- /litellm_.*/
|
||||
- e2e_ui_testing:
|
||||
requires:
|
||||
- ui_build
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ LiteLLM supports MCP for agent workflows:
|
|||
- Support for external MCP servers (Zapier, Jira, Linear, etc.)
|
||||
- See `litellm/experimental_mcp_client/` and `litellm/proxy/_experimental/mcp_server/`
|
||||
|
||||
## RUNNING SCRIPTS
|
||||
|
||||
Use `poetry run python script.py` to run Python scripts in the project environment (for non-test files).
|
||||
|
||||
## TESTING CONSIDERATIONS
|
||||
|
||||
1. **Provider Tests**: Test against real provider APIs when possible
|
||||
|
|
|
|||
|
|
@ -25,6 +25,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
- `poetry run pytest tests/path/to/test_file.py -v` - Run specific test file
|
||||
- `poetry run pytest tests/path/to/test_file.py::test_function -v` - Run specific test
|
||||
|
||||
### Running Scripts
|
||||
- `poetry run python script.py` - Run Python scripts (use for non-test files)
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
LiteLLM is a unified interface for 100+ LLM providers with two main components:
|
||||
|
|
|
|||
6
Makefile
|
|
@ -34,13 +34,13 @@ install-proxy-dev:
|
|||
|
||||
# CI-compatible installations (matches GitHub workflows exactly)
|
||||
install-dev-ci:
|
||||
pip install openai==1.99.5
|
||||
pip install openai==2.8.0
|
||||
poetry install --with dev
|
||||
pip install openai==1.99.5
|
||||
pip install openai==2.8.0
|
||||
|
||||
install-proxy-dev-ci:
|
||||
poetry install --with dev,proxy-dev --extras proxy
|
||||
pip install openai==1.99.5
|
||||
pip install openai==2.8.0
|
||||
|
||||
install-test-deps: install-proxy-dev
|
||||
poetry run pip install "pytest-retry==1.6.3"
|
||||
|
|
|
|||
|
|
@ -1,261 +0,0 @@
|
|||
# Vertex AI Environment Variables Setup Guide
|
||||
|
||||
## Overview
|
||||
|
||||
LiteLLM can load Vertex AI credentials from environment variables instead of storing them in config files. This is more secure and easier to manage for local development.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
LiteLLM looks for these environment variables (in order of precedence):
|
||||
|
||||
### 1. **DEFAULT_VERTEXAI_PROJECT** (Required)
|
||||
Your GCP project ID that has Vertex AI enabled.
|
||||
|
||||
```bash
|
||||
export DEFAULT_VERTEXAI_PROJECT="my-gcp-project-id"
|
||||
```
|
||||
|
||||
### 2. **DEFAULT_VERTEXAI_LOCATION** (Required)
|
||||
The region/location for Vertex AI services.
|
||||
|
||||
```bash
|
||||
export DEFAULT_VERTEXAI_LOCATION="global"
|
||||
# or
|
||||
export DEFAULT_VERTEXAI_LOCATION="us-central1"
|
||||
```
|
||||
|
||||
Common locations:
|
||||
- `global` - For Discovery Engine and global services
|
||||
- `us-central1` - US Central region
|
||||
- `us-east1` - US East region
|
||||
- `europe-west1` - Europe West region
|
||||
- `asia-southeast1` - Asia Southeast region
|
||||
|
||||
### 3. **DEFAULT_GOOGLE_APPLICATION_CREDENTIALS** (Required)
|
||||
Path to your service account JSON key file.
|
||||
|
||||
```bash
|
||||
export DEFAULT_GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
|
||||
```
|
||||
|
||||
### 4. **GOOGLE_APPLICATION_CREDENTIALS** (Fallback)
|
||||
Standard Google Cloud environment variable (used as fallback).
|
||||
|
||||
```bash
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
|
||||
```
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### Option 1: Interactive Script
|
||||
|
||||
```bash
|
||||
chmod +x setup_vertex_env.sh
|
||||
source setup_vertex_env.sh
|
||||
```
|
||||
|
||||
### Option 2: Manual Setup
|
||||
|
||||
1. **Set environment variables** (for current session):
|
||||
|
||||
```bash
|
||||
export DEFAULT_VERTEXAI_PROJECT="your-project-id"
|
||||
export DEFAULT_VERTEXAI_LOCATION="global"
|
||||
export DEFAULT_GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp/service-account.json"
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp/service-account.json"
|
||||
```
|
||||
|
||||
2. **Make them persistent** (add to `~/.zshrc` or `~/.bashrc`):
|
||||
|
||||
```bash
|
||||
echo 'export DEFAULT_VERTEXAI_PROJECT="your-project-id"' >> ~/.zshrc
|
||||
echo 'export DEFAULT_VERTEXAI_LOCATION="global"' >> ~/.zshrc
|
||||
echo 'export DEFAULT_GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp/service-account.json"' >> ~/.zshrc
|
||||
echo 'export GOOGLE_APPLICATION_CREDENTIALS="$HOME/.gcp/service-account.json"' >> ~/.zshrc
|
||||
```
|
||||
|
||||
3. **Reload your shell**:
|
||||
|
||||
```bash
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
## Service Account Setup
|
||||
|
||||
### 1. Create a Service Account
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts create litellm-vertex-sa \
|
||||
--display-name="LiteLLM Vertex AI Service Account"
|
||||
```
|
||||
|
||||
### 2. Grant Necessary Permissions
|
||||
|
||||
For Discovery Engine (vector stores):
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:litellm-vertex-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/discoveryengine.viewer"
|
||||
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:litellm-vertex-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/discoveryengine.dataStoreEditor"
|
||||
```
|
||||
|
||||
For general Vertex AI:
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:litellm-vertex-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/aiplatform.user"
|
||||
```
|
||||
|
||||
### 3. Create and Download Key
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts keys create ~/service-account-key.json \
|
||||
--iam-account=litellm-vertex-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
## Verify Setup
|
||||
|
||||
### Check Environment Variables
|
||||
|
||||
```bash
|
||||
python3 << 'EOF'
|
||||
import os
|
||||
print("✓ Environment Variables:")
|
||||
print(f" DEFAULT_VERTEXAI_PROJECT: {os.getenv('DEFAULT_VERTEXAI_PROJECT')}")
|
||||
print(f" DEFAULT_VERTEXAI_LOCATION: {os.getenv('DEFAULT_VERTEXAI_LOCATION')}")
|
||||
print(f" DEFAULT_GOOGLE_APPLICATION_CREDENTIALS: {os.getenv('DEFAULT_GOOGLE_APPLICATION_CREDENTIALS')}")
|
||||
print(f" GOOGLE_APPLICATION_CREDENTIALS: {os.getenv('GOOGLE_APPLICATION_CREDENTIALS')}")
|
||||
|
||||
# Check if credentials file exists
|
||||
creds_path = os.getenv('DEFAULT_GOOGLE_APPLICATION_CREDENTIALS')
|
||||
if creds_path and os.path.exists(creds_path):
|
||||
print(f"\n✅ Credentials file found at: {creds_path}")
|
||||
else:
|
||||
print(f"\n❌ Credentials file NOT found at: {creds_path}")
|
||||
EOF
|
||||
```
|
||||
|
||||
### Test Authentication
|
||||
|
||||
```bash
|
||||
python3 << 'EOF'
|
||||
import os
|
||||
import json
|
||||
from google.oauth2 import service_account
|
||||
from google.auth.transport.requests import Request
|
||||
|
||||
creds_path = os.getenv('DEFAULT_GOOGLE_APPLICATION_CREDENTIALS')
|
||||
project = os.getenv('DEFAULT_VERTEXAI_PROJECT')
|
||||
|
||||
try:
|
||||
# Load credentials
|
||||
credentials = service_account.Credentials.from_service_account_file(
|
||||
creds_path,
|
||||
scopes=['https://www.googleapis.com/auth/cloud-platform']
|
||||
)
|
||||
|
||||
# Get access token
|
||||
credentials.refresh(Request())
|
||||
|
||||
print("✅ Authentication successful!")
|
||||
print(f" Project: {project}")
|
||||
print(f" Service Account: {credentials.service_account_email}")
|
||||
print(f" Token expiry: {credentials.expiry}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Authentication failed: {e}")
|
||||
EOF
|
||||
```
|
||||
|
||||
## Using with Vector Store Passthrough
|
||||
|
||||
Once your environment is set up, the vector store passthrough will work in two ways:
|
||||
|
||||
### 1. **With Vector Store Config** (Priority 1)
|
||||
If you have a vector store configured with its own credentials in `litellm_params`, those will be used first:
|
||||
|
||||
```yaml
|
||||
vector_stores:
|
||||
- vector_store_id: test-store-123
|
||||
custom_llm_provider: vertex_ai
|
||||
litellm_params:
|
||||
vertex_project: "specific-project"
|
||||
vertex_location: "us-central1"
|
||||
vertex_credentials: "{...}" # Inline credentials
|
||||
```
|
||||
|
||||
### 2. **Environment Variables Fallback** (Priority 2)
|
||||
If the vector store doesn't have explicit credentials, it falls back to your environment variables:
|
||||
|
||||
```yaml
|
||||
vector_stores:
|
||||
- vector_store_id: test-store-123
|
||||
custom_llm_provider: vertex_ai
|
||||
# No litellm_params - will use DEFAULT_VERTEXAI_PROJECT, DEFAULT_VERTEXAI_LOCATION, etc.
|
||||
```
|
||||
|
||||
### 3. **Model Config Fallback** (Priority 3)
|
||||
If neither above work, it looks for credentials in your model configuration.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No credentials found"
|
||||
|
||||
Check that all environment variables are set:
|
||||
```bash
|
||||
env | grep -E "(DEFAULT_VERTEXAI|GOOGLE_APPLICATION_CREDENTIALS)"
|
||||
```
|
||||
|
||||
### "Authentication failed"
|
||||
|
||||
Verify your service account key is valid:
|
||||
```bash
|
||||
cat $DEFAULT_GOOGLE_APPLICATION_CREDENTIALS | python3 -m json.tool
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
|
||||
Ensure your service account has the necessary roles:
|
||||
```bash
|
||||
gcloud projects get-iam-policy YOUR_PROJECT_ID \
|
||||
--flatten="bindings[].members" \
|
||||
--filter="bindings.members:serviceAccount:litellm-vertex-sa@*"
|
||||
```
|
||||
|
||||
### Different Credentials for Different Projects
|
||||
|
||||
If you need to use different credentials for different vector stores, configure them explicitly in the vector store config rather than relying on environment variables.
|
||||
|
||||
## Start LiteLLM Proxy
|
||||
|
||||
Once your environment is configured:
|
||||
|
||||
```bash
|
||||
# Start the proxy (it will automatically load env vars)
|
||||
litellm --config proxy_server_config.yaml
|
||||
|
||||
# Or with debug logging
|
||||
export LITELLM_LOG=DEBUG
|
||||
litellm --config proxy_server_config.yaml
|
||||
```
|
||||
|
||||
You should see logs like:
|
||||
```
|
||||
Vertex: Loading vertex credentials from /path/to/service-account.json
|
||||
Found credentials for vertex_ai_default
|
||||
```
|
||||
|
||||
## Test the Endpoint
|
||||
|
||||
```bash
|
||||
curl -X POST http://0.0.0.0:4000/vertex_ai/discovery/v1/projects/fake-project/locations/global/dataStores/test-store-123/servingConfigs/default_config:search \
|
||||
-H 'Authorization: Bearer YOUR_LITELLM_API_KEY' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"query": "test query"}'
|
||||
```
|
||||
|
||||
The proxy will use your environment credentials to make the request to Vertex AI!
|
||||
|
||||
12
cookbook/LiteLLM_CometAPI.ipynb
vendored
|
|
@ -28,7 +28,7 @@
|
|||
"Requirement already satisfied: importlib-metadata>=6.8.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (8.6.1)\n",
|
||||
"Requirement already satisfied: jinja2<4.0.0,>=3.1.2 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (3.1.6)\n",
|
||||
"Requirement already satisfied: jsonschema<5.0.0,>=4.22.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (4.25.1)\n",
|
||||
"Requirement already satisfied: openai>=1.99.5 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (1.109.1)\n",
|
||||
"Requirement already satisfied: openai>=2.8.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (1.109.1)\n",
|
||||
"Requirement already satisfied: pydantic<3.0.0,>=2.5.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (2.11.10)\n",
|
||||
"Requirement already satisfied: python-dotenv>=0.2.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (1.1.1)\n",
|
||||
"Requirement already satisfied: tiktoken>=0.7.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from litellm) (0.12.0)\n",
|
||||
|
|
@ -50,11 +50,11 @@
|
|||
"Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (2025.9.1)\n",
|
||||
"Requirement already satisfied: referencing>=0.28.4 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (0.36.2)\n",
|
||||
"Requirement already satisfied: rpds-py>=0.7.1 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm) (0.27.1)\n",
|
||||
"Requirement already satisfied: distro<2,>=1.7.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=1.99.5->litellm) (1.9.0)\n",
|
||||
"Requirement already satisfied: jiter<1,>=0.4.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=1.99.5->litellm) (0.11.0)\n",
|
||||
"Requirement already satisfied: sniffio in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=1.99.5->litellm) (1.3.1)\n",
|
||||
"Requirement already satisfied: tqdm>4 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=1.99.5->litellm) (4.67.1)\n",
|
||||
"Requirement already satisfied: typing-extensions<5,>=4.11 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=1.99.5->litellm) (4.15.0)\n",
|
||||
"Requirement already satisfied: distro<2,>=1.7.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=2.8.0->litellm) (1.9.0)\n",
|
||||
"Requirement already satisfied: jiter<1,>=0.4.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=2.8.0->litellm) (0.11.0)\n",
|
||||
"Requirement already satisfied: sniffio in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=2.8.0->litellm) (1.3.1)\n",
|
||||
"Requirement already satisfied: tqdm>4 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=2.8.0->litellm) (4.67.1)\n",
|
||||
"Requirement already satisfied: typing-extensions<5,>=4.11 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from openai>=2.8.0->litellm) (4.15.0)\n",
|
||||
"Requirement already satisfied: annotated-types>=0.6.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.5.0->litellm) (0.7.0)\n",
|
||||
"Requirement already satisfied: pydantic-core==2.33.2 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.5.0->litellm) (2.33.2)\n",
|
||||
"Requirement already satisfied: typing-inspection>=0.4.0 in /Users/xmx/.miniforge3/lib/python3.12/site-packages (from pydantic<3.0.0,>=2.5.0->litellm) (0.4.2)\n",
|
||||
|
|
|
|||
2
cookbook/LiteLLM_HuggingFace.ipynb
vendored
|
|
@ -131,7 +131,7 @@
|
|||
" {\n",
|
||||
" \"type\": \"image_url\",\n",
|
||||
" \"image_url\": {\n",
|
||||
" \"url\": \"https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg\",\n",
|
||||
" \"url\": \"https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png\",\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ type: application
|
|||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.4.7
|
||||
version: 0.4.8
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ spec:
|
|||
metadata:
|
||||
labels:
|
||||
{{- include "litellm.labels" . | nindent 8 }}
|
||||
{{- with .Values.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
annotations:
|
||||
{{- with .Values.migrationJob.annotations }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,10 @@ WORKDIR /app
|
|||
USER root
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache gcc python3-dev openssl openssl-dev
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
python3-dev \
|
||||
openssl-dev
|
||||
|
||||
|
||||
RUN pip install --upgrade pip && \
|
||||
|
|
|
|||
|
|
@ -21,11 +21,14 @@ ENV LITELLM_NON_ROOT=true
|
|||
|
||||
# Build Admin UI
|
||||
RUN mkdir -p /tmp/litellm_ui && \
|
||||
npm install -g npm@latest && \
|
||||
npm cache clean --force && \
|
||||
cd ui/litellm-dashboard && \
|
||||
if [ -f "../../enterprise/enterprise_ui/enterprise_colors.json" ]; then \
|
||||
cp ../../enterprise/enterprise_ui/enterprise_colors.json ./ui_colors.json; \
|
||||
fi && \
|
||||
npm install && \
|
||||
rm -f package-lock.json && \
|
||||
npm install --legacy-peer-deps && \
|
||||
npm run build && \
|
||||
cp -r ./out/* /tmp/litellm_ui/ && \
|
||||
cd /tmp/litellm_ui && \
|
||||
|
|
|
|||
24
docs/my-website/blog/authors.yml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
litellm:
|
||||
name: LiteLLM Team
|
||||
title: LiteLLM Core Team
|
||||
url: https://github.com/BerriAI/litellm
|
||||
image_url: https://github.com/BerriAI.png
|
||||
|
||||
krrish:
|
||||
name: Krrish Dholakia
|
||||
title: CEO, LiteLLM
|
||||
url: https://www.linkedin.com/in/krish-d/
|
||||
image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg
|
||||
|
||||
ishaan:
|
||||
name: Ishaan Jaffer
|
||||
title: CTO, LiteLLM
|
||||
url: https://www.linkedin.com/in/reffajnaahsi/
|
||||
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
|
||||
|
||||
# Alias for typo in name
|
||||
ishaan-alt:
|
||||
name: Ishaan Jaff
|
||||
title: CTO, LiteLLM
|
||||
url: https://www.linkedin.com/in/reffajnaahsi/
|
||||
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
|
||||
982
docs/my-website/blog/gemini_3/index.md
Normal file
|
|
@ -0,0 +1,982 @@
|
|||
---
|
||||
slug: gemini_3
|
||||
title: "DAY 0 Support: Gemini 3 on LiteLLM"
|
||||
date: 2025-11-19T10:00:00
|
||||
authors:
|
||||
- name: Sameer Kankute
|
||||
title: SWE @ LiteLLM (LLM Translation)
|
||||
url: https://www.linkedin.com/in/sameer-kankute/
|
||||
image_url: https://media.licdn.com/dms/image/v2/D4D03AQHB_loQYd5gjg/profile-displayphoto-shrink_800_800/profile-displayphoto-shrink_800_800/0/1719137160975?e=1765411200&v=beta&t=c8396f--_lH6Fb_pVvx_jGholPfcl0bvwmNynbNdnII
|
||||
- name: Krrish Dholakia
|
||||
title: "CEO, LiteLLM"
|
||||
url: https://www.linkedin.com/in/krish-d/
|
||||
image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg
|
||||
- name: Ishaan Jaff
|
||||
title: "CTO, LiteLLM"
|
||||
url: https://www.linkedin.com/in/reffajnaahsi/
|
||||
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
|
||||
tags: [gemini, day 0 support, llms]
|
||||
hide_table_of_contents: false
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
:::info
|
||||
|
||||
This guide covers common questions and best practices for using `gemini-3-pro-preview` with LiteLLM Proxy and SDK.
|
||||
|
||||
:::
|
||||
|
||||
## Quick Start
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="Python SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
os.environ["GEMINI_API_KEY"] = "your-api-key"
|
||||
|
||||
response = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="LiteLLM Proxy">
|
||||
|
||||
**1. Add to config.yaml:**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gemini-3-pro-preview
|
||||
litellm_params:
|
||||
model: gemini/gemini-3-pro-preview
|
||||
api_key: os.environ/GEMINI_API_KEY
|
||||
```
|
||||
|
||||
**2. Start proxy:**
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
**3. Make request:**
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [{"role": "user", "content": "Hello!"}],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Supported Endpoints
|
||||
|
||||
LiteLLM provides **full end-to-end support** for Gemini 3 Pro Preview on:
|
||||
|
||||
- ✅ `/v1/chat/completions` - OpenAI-compatible chat completions endpoint
|
||||
- ✅ `/v1/responses` - OpenAI Responses API endpoint (streaming and non-streaming)
|
||||
- ✅ [`/v1/messages`](../../docs/anthropic_unified) - Anthropic-compatible messages endpoint
|
||||
- ✅ `/v1/generateContent` – [Google Gemini API](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/gemini#rest) compatible endpoint (for code, see: `client.models.generate_content(...)`)
|
||||
|
||||
All endpoints support:
|
||||
- Streaming and non-streaming responses
|
||||
- Function calling with thought signatures
|
||||
- Multi-turn conversations
|
||||
- All Gemini 3-specific features
|
||||
|
||||
## Thought Signatures
|
||||
|
||||
#### What are Thought Signatures?
|
||||
|
||||
Thought signatures are encrypted representations of the model's internal reasoning process. They're essential for maintaining context across multi-turn conversations, especially with function calling.
|
||||
|
||||
#### How Thought Signatures Work
|
||||
|
||||
1. **Automatic Extraction**: When Gemini 3 returns a function call, LiteLLM automatically extracts the `thought_signature` from the response
|
||||
2. **Storage**: Thought signatures are stored in `provider_specific_fields.thought_signature` of tool calls
|
||||
3. **Automatic Preservation**: When you include the assistant's message in conversation history, LiteLLM automatically preserves and returns thought signatures to Gemini
|
||||
|
||||
## Example: Multi-Turn Function Calling
|
||||
|
||||
#### Streaming with Thought Signatures
|
||||
|
||||
When using streaming mode with `stream_chunk_builder()`, thought signatures are now automatically preserved:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="streaming" label="Streaming SDK">
|
||||
|
||||
```python
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
os.environ["GEMINI_API_KEY"] = "your-api-key"
|
||||
|
||||
MODEL = "gemini/gemini-3-pro-preview"
|
||||
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant. Use the calculate tool."},
|
||||
{"role": "user", "content": "What is 2+2?"},
|
||||
]
|
||||
|
||||
tools = [{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "calculate",
|
||||
"description": "Calculate a mathematical expression",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"expression": {"type": "string"}},
|
||||
"required": ["expression"],
|
||||
},
|
||||
},
|
||||
}]
|
||||
|
||||
print("Step 1: Sending request with stream=True...")
|
||||
response = completion(
|
||||
model=MODEL,
|
||||
messages=messages,
|
||||
stream=True,
|
||||
tools=tools,
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
# Collect all chunks
|
||||
chunks = []
|
||||
for part in response:
|
||||
chunks.append(part)
|
||||
|
||||
# Reconstruct message using stream_chunk_builder
|
||||
# Thought signatures are now preserved automatically!
|
||||
full_response = litellm.stream_chunk_builder(chunks, messages=messages)
|
||||
print(f"Full response: {full_response}")
|
||||
|
||||
assistant_msg = full_response.choices[0].message
|
||||
|
||||
# ✅ Thought signature is now preserved in provider_specific_fields
|
||||
if assistant_msg.tool_calls and assistant_msg.tool_calls[0].provider_specific_fields:
|
||||
thought_sig = assistant_msg.tool_calls[0].provider_specific_fields.get("thought_signature")
|
||||
print(f"Thought signature preserved: {thought_sig is not None}")
|
||||
|
||||
# Append assistant message (includes thought signatures automatically)
|
||||
messages.append(assistant_msg)
|
||||
|
||||
# Mock tool execution
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
"content": "4",
|
||||
"tool_call_id": assistant_msg.tool_calls[0].id
|
||||
})
|
||||
|
||||
print("\nStep 2: Sending tool result back to model...")
|
||||
response_2 = completion(
|
||||
model=MODEL,
|
||||
messages=messages,
|
||||
stream=True,
|
||||
tools=tools,
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
for part in response_2:
|
||||
if part.choices[0].delta.content:
|
||||
print(part.choices[0].delta.content, end="")
|
||||
print() # New line
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- ✅ `stream_chunk_builder()` now preserves `provider_specific_fields` including thought signatures
|
||||
- ✅ Thought signatures are automatically included when appending `assistant_msg` to conversation history
|
||||
- ✅ Multi-turn conversations work seamlessly with streaming
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="sdk" label="Non-Streaming SDK">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
client = OpenAI(api_key="sk-1234", base_url="http://localhost:4000")
|
||||
|
||||
# Define tools
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Step 1: Initial request
|
||||
messages = [{"role": "user", "content": "What's the weather in Tokyo?"}]
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gemini-3-pro-preview",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
# Step 2: Append assistant message (thought signatures automatically preserved)
|
||||
messages.append(response.choices[0].message)
|
||||
|
||||
# Step 3: Execute tool and append result
|
||||
for tool_call in response.choices[0].message.tool_calls:
|
||||
if tool_call.function.name == "get_weather":
|
||||
result = {"temperature": 30, "unit": "celsius"}
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
"content": json.dumps(result),
|
||||
"tool_call_id": tool_call.id
|
||||
})
|
||||
|
||||
# Step 4: Follow-up request (thought signatures automatically included)
|
||||
response2 = client.chat.completions.create(
|
||||
model="gemini-3-pro-preview",
|
||||
messages=messages,
|
||||
tools=tools,
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
print(response2.choices[0].message.content)
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- ✅ Thought signatures are automatically extracted from `response.choices[0].message.tool_calls[].provider_specific_fields.thought_signature`
|
||||
- ✅ When you append `response.choices[0].message` to your conversation history, thought signatures are automatically preserved
|
||||
- ✅ You don't need to manually extract or manage thought signatures
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="cURL">
|
||||
|
||||
```bash
|
||||
# Step 1: Initial request
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [
|
||||
{"role": "user", "content": "What'\''s the weather in Tokyo?"}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the current weather",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response includes thought signature:**
|
||||
|
||||
```json
|
||||
{
|
||||
"choices": [{
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"tool_calls": [{
|
||||
"id": "call_abc123",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": "{\"location\": \"Tokyo\"}"
|
||||
},
|
||||
"provider_specific_fields": {
|
||||
"thought_signature": "CpcHAdHtim9+q4rstcbvQC0ic4x1/vqQlCJWgE+UZ6dTLYGHMMBkF/AxqL5UmP6SY46uYC8t4BTFiXG5zkw6EMJ..."
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Step 2: Follow-up request (include assistant message with thought signature)
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [
|
||||
{"role": "user", "content": "What'\''s the weather in Tokyo?"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [{
|
||||
"id": "call_abc123",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": "{\"location\": \"Tokyo\"}"
|
||||
},
|
||||
"provider_specific_fields": {
|
||||
"thought_signature": "CpcHAdHtim9+q4rstcbvQC0ic4x1/vqQlCJWgE+UZ6dTLYGHMMBkF/AxqL5UmP6SY46uYC8t4BTFiXG5zkw6EMJ..."
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "{\"temperature\": 30, \"unit\": \"celsius\"}",
|
||||
"tool_call_id": "call_abc123"
|
||||
}
|
||||
],
|
||||
"tools": [...],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### Important Notes on Thought Signatures
|
||||
|
||||
1. **Automatic Handling**: LiteLLM automatically extracts and preserves thought signatures. You don't need to manually manage them.
|
||||
|
||||
2. **Parallel Function Calls**: When the model makes parallel function calls, only the **first function call** has a thought signature.
|
||||
|
||||
3. **Sequential Function Calls**: In multi-step function calling, each step's first function call has its own thought signature that must be preserved.
|
||||
|
||||
4. **Required for Context**: Thought signatures are essential for maintaining reasoning context. Without them, the model may lose context of its previous reasoning.
|
||||
|
||||
## Conversation History: Switching from Non-Gemini-3 Models
|
||||
|
||||
#### Common Question: Will switching from a non-Gemini-3 model to Gemini-3 break conversation history?
|
||||
|
||||
**Answer: No!** LiteLLM automatically handles this by adding dummy thought signatures when needed.
|
||||
|
||||
#### How It Works
|
||||
|
||||
When you switch from a model that doesn't use thought signatures (e.g., `gemini-2.5-flash`) to Gemini 3, LiteLLM:
|
||||
|
||||
1. **Detects missing signatures**: Identifies assistant messages with tool calls that lack thought signatures
|
||||
2. **Adds dummy signature**: Automatically injects a dummy thought signature (`skip_thought_signature_validator`) for compatibility
|
||||
3. **Maintains conversation flow**: Your conversation history continues to work seamlessly
|
||||
|
||||
#### Example: Switching Models Mid-Conversation
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="Python SDK">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(api_key="sk-1234", base_url="http://localhost:4000")
|
||||
|
||||
# Step 1: Start with gemini-2.5-flash (no thought signatures)
|
||||
messages = [{"role": "user", "content": "What's the weather?"}]
|
||||
|
||||
response1 = client.chat.completions.create(
|
||||
model="gemini-2.5-flash",
|
||||
messages=messages,
|
||||
tools=[...],
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
# Append assistant message (no tool call thought signature from gemini-2.5-flash)
|
||||
messages.append(response1.choices[0].message)
|
||||
|
||||
# Step 2: Switch to gemini-3-pro-preview
|
||||
# LiteLLM automatically adds dummy thought signature to the previous assistant message
|
||||
response2 = client.chat.completions.create(
|
||||
model="gemini-3-pro-preview", # 👈 Switched model
|
||||
messages=messages, # 👈 Same conversation history
|
||||
tools=[...],
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
# ✅ Works seamlessly! No errors, no breaking changes
|
||||
print(response2.choices[0].message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="cURL">
|
||||
|
||||
```bash
|
||||
# Step 1: Start with gemini-2.5-flash
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-2.5-flash",
|
||||
"messages": [{"role": "user", "content": "What'\''s the weather?"}],
|
||||
"tools": [...],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
|
||||
# Step 2: Switch to gemini-3-pro-preview with same conversation history
|
||||
# LiteLLM automatically handles the missing thought signature
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview", # 👈 Switched model
|
||||
"messages": [
|
||||
{"role": "user", "content": "What'\''s the weather?"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"tool_calls": [...] # 👈 No thought_signature from gemini-2.5-flash
|
||||
}
|
||||
],
|
||||
"tools": [...],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
# ✅ Works! LiteLLM adds dummy signature automatically
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
#### Dummy Signature Details
|
||||
|
||||
The dummy signature used is: `base64("skip_thought_signature_validator")`
|
||||
|
||||
This is the recommended approach by Google for handling conversation history from models that don't support thought signatures. It allows Gemini 3 to:
|
||||
- Accept the conversation history without validation errors
|
||||
- Continue the conversation seamlessly
|
||||
- Maintain context across model switches
|
||||
|
||||
## Thinking Level Parameter
|
||||
|
||||
#### How `reasoning_effort` Maps to `thinking_level`
|
||||
|
||||
For Gemini 3 Pro Preview, LiteLLM automatically maps `reasoning_effort` to the new `thinking_level` parameter:
|
||||
|
||||
| `reasoning_effort` | `thinking_level` | Notes |
|
||||
|-------------------|------------------|-------|
|
||||
| `"minimal"` | `"low"` | Maps to low thinking level |
|
||||
| `"low"` | `"low"` | Default for most use cases |
|
||||
| `"medium"` | `"high"` | Medium not available yet, maps to high |
|
||||
| `"high"` | `"high"` | Maximum reasoning depth |
|
||||
| `"disable"` | `"low"` | Gemini 3 cannot fully disable thinking |
|
||||
| `"none"` | `"low"` | Gemini 3 cannot fully disable thinking |
|
||||
|
||||
#### Default Behavior
|
||||
|
||||
If you don't specify `reasoning_effort`, LiteLLM automatically sets `thinking_level="low"` for Gemini 3 models, to avoid high costs.
|
||||
|
||||
### Example Usage
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="Python SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
# Low thinking level (faster, lower cost)
|
||||
response = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "What's the weather?"}],
|
||||
reasoning_effort="low" # Maps to thinking_level="low"
|
||||
)
|
||||
|
||||
# High thinking level (deeper reasoning, higher cost)
|
||||
response = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "Solve this complex math problem step by step."}],
|
||||
reasoning_effort="high" # Maps to thinking_level="high"
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="LiteLLM Proxy">
|
||||
|
||||
```bash
|
||||
# Low thinking level
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [{"role": "user", "content": "What'\''s the weather?"}],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
|
||||
# High thinking level
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [{"role": "user", "content": "Solve this complex problem."}],
|
||||
"reasoning_effort": "high"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Gemini 3 Cannot Disable Thinking**: Unlike Gemini 2.5 models, Gemini 3 cannot fully disable thinking. Even when you set `reasoning_effort="none"` or `"disable"`, it maps to `thinking_level="low"`.
|
||||
|
||||
2. **Temperature Recommendation**: For Gemini 3 models, LiteLLM defaults `temperature` to `1.0` and strongly recommends keeping it at this default. Setting `temperature < 1.0` can cause:
|
||||
- Infinite loops
|
||||
- Degraded reasoning performance
|
||||
- Failure on complex tasks
|
||||
|
||||
3. **Automatic Defaults**: If you don't specify `reasoning_effort`, LiteLLM automatically sets `thinking_level="low"` for optimal performance.
|
||||
|
||||
## Cost Tracking: Prompt Caching & Context Window
|
||||
|
||||
LiteLLM provides comprehensive cost tracking for Gemini 3 Pro Preview, including support for prompt caching and tiered pricing based on context window size.
|
||||
|
||||
### Prompt Caching Cost Tracking
|
||||
|
||||
Gemini 3 supports prompt caching, which allows you to cache frequently used prompt prefixes to reduce costs. LiteLLM automatically tracks and calculates costs for:
|
||||
|
||||
- **Cache Hit Tokens**: Tokens that are read from cache (charged at a lower rate)
|
||||
- **Cache Creation Tokens**: Tokens that are written to cache (one-time cost)
|
||||
- **Text Tokens**: Regular prompt tokens that are processed normally
|
||||
|
||||
#### How It Works
|
||||
|
||||
LiteLLM extracts caching information from the `prompt_tokens_details` field in the usage object:
|
||||
|
||||
```python
|
||||
{
|
||||
"usage": {
|
||||
"prompt_tokens": 50000,
|
||||
"completion_tokens": 1000,
|
||||
"total_tokens": 51000,
|
||||
"prompt_tokens_details": {
|
||||
"cached_tokens": 30000, # Cache hit tokens
|
||||
"cache_creation_tokens": 5000, # Tokens written to cache
|
||||
"text_tokens": 15000 # Regular processed tokens
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Context Window Tiered Pricing
|
||||
|
||||
Gemini 3 Pro Preview supports up to 1M tokens of context, with tiered pricing that automatically applies when your prompt exceeds 200k tokens.
|
||||
|
||||
#### Automatic Tier Detection
|
||||
|
||||
LiteLLM automatically detects when your prompt exceeds the 200k token threshold and applies the appropriate tiered pricing:
|
||||
|
||||
```python
|
||||
from litellm import completion_cost
|
||||
|
||||
# Example: Small prompt (< 200k tokens)
|
||||
response_small = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
# Uses base pricing: $0.000002/input token, $0.000012/output token
|
||||
|
||||
# Example: Large prompt (> 200k tokens)
|
||||
response_large = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "..." * 250000}] # 250k tokens
|
||||
)
|
||||
# Automatically uses tiered pricing: $0.000004/input token, $0.000018/output token
|
||||
```
|
||||
|
||||
#### Cost Breakdown
|
||||
|
||||
The cost calculation includes:
|
||||
|
||||
1. **Text Processing Cost**: Regular tokens processed at base or tiered rate
|
||||
2. **Cache Read Cost**: Cached tokens read at discounted rate
|
||||
3. **Cache Creation Cost**: One-time cost for writing tokens to cache (applies tiered rate if above 200k)
|
||||
4. **Output Cost**: Generated tokens at base or tiered rate
|
||||
|
||||
### Example: Viewing Cost Breakdown
|
||||
|
||||
You can view the detailed cost breakdown using LiteLLM's cost tracking:
|
||||
|
||||
```python
|
||||
from litellm import completion, completion_cost
|
||||
|
||||
response = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "Explain prompt caching"}],
|
||||
caching=True # Enable prompt caching
|
||||
)
|
||||
|
||||
# Get total cost
|
||||
total_cost = completion_cost(completion_response=response)
|
||||
print(f"Total cost: ${total_cost:.6f}")
|
||||
|
||||
# Access usage details
|
||||
usage = response.usage
|
||||
print(f"Prompt tokens: {usage.prompt_tokens}")
|
||||
print(f"Completion tokens: {usage.completion_tokens}")
|
||||
|
||||
# Access caching details
|
||||
if usage.prompt_tokens_details:
|
||||
print(f"Cache hit tokens: {usage.prompt_tokens_details.cached_tokens}")
|
||||
print(f"Cache creation tokens: {usage.prompt_tokens_details.cache_creation_tokens}")
|
||||
print(f"Text tokens: {usage.prompt_tokens_details.text_tokens}")
|
||||
```
|
||||
|
||||
### Cost Optimization Tips
|
||||
|
||||
1. **Use Prompt Caching**: For repeated prompt prefixes, enable caching to reduce costs by up to 90% for cached portions
|
||||
2. **Monitor Context Size**: Be aware that prompts above 200k tokens use tiered pricing (2x for input, 1.5x for output)
|
||||
3. **Cache Management**: Cache creation tokens are charged once when writing to cache, then subsequent reads are much cheaper
|
||||
4. **Track Usage**: Use LiteLLM's built-in cost tracking to monitor spending across different token types
|
||||
|
||||
### Integration with LiteLLM Proxy
|
||||
|
||||
When using LiteLLM Proxy, all cost tracking is automatically logged and available through:
|
||||
|
||||
- **Usage Logs**: Detailed token and cost breakdowns in proxy logs
|
||||
- **Budget Management**: Set budgets and alerts based on actual usage
|
||||
- **Analytics Dashboard**: View cost trends and breakdowns by token type
|
||||
|
||||
```yaml
|
||||
# config.yaml
|
||||
model_list:
|
||||
- model_name: gemini-3-pro-preview
|
||||
litellm_params:
|
||||
model: gemini/gemini-3-pro-preview
|
||||
api_key: os.environ/GEMINI_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
# Enable detailed cost tracking
|
||||
success_callback: ["langfuse"] # or your preferred logging service
|
||||
```
|
||||
|
||||
## Using with Claude Code CLI
|
||||
|
||||
You can use `gemini-3-pro-preview` with **Claude Code CLI** - Anthropic's command-line interface. This allows you to use Gemini 3 Pro Preview with Claude Code's native syntax and workflows.
|
||||
|
||||
### Setup
|
||||
|
||||
**1. Add Gemini 3 Pro Preview to your `config.yaml`:**
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gemini-3-pro-preview
|
||||
litellm_params:
|
||||
model: gemini/gemini-3-pro-preview
|
||||
api_key: os.environ/GEMINI_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
master_key: os.environ/LITELLM_MASTER_KEY
|
||||
```
|
||||
|
||||
**2. Set environment variables:**
|
||||
|
||||
```bash
|
||||
export GEMINI_API_KEY="your-gemini-api-key"
|
||||
export LITELLM_MASTER_KEY="sk-1234567890" # Generate a secure key
|
||||
```
|
||||
|
||||
**3. Start LiteLLM Proxy:**
|
||||
|
||||
```bash
|
||||
litellm --config /path/to/config.yaml
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
**4. Configure Claude Code to use LiteLLM Proxy:**
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL="http://0.0.0.0:4000"
|
||||
export ANTHROPIC_AUTH_TOKEN="$LITELLM_MASTER_KEY"
|
||||
```
|
||||
|
||||
**5. Use Gemini 3 Pro Preview with Claude Code:**
|
||||
|
||||
```bash
|
||||
# Claude Code will use gemini-3-pro-preview from your LiteLLM proxy
|
||||
claude --model gemini-3-pro-preview
|
||||
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
|
||||
Once configured, you can interact with Gemini 3 Pro Preview using Claude Code's native interface:
|
||||
|
||||
```bash
|
||||
$ claude --model gemini-3-pro-preview
|
||||
> Explain how thought signatures work in multi-turn conversations.
|
||||
|
||||
# Gemini 3 Pro Preview responds through Claude Code interface
|
||||
```
|
||||
|
||||
### Benefits
|
||||
|
||||
- ✅ **Native Claude Code Experience**: Use Gemini 3 Pro Preview with Claude Code's familiar CLI interface
|
||||
- ✅ **Unified Authentication**: Single API key for all models through LiteLLM proxy
|
||||
- ✅ **Cost Tracking**: All usage tracked through LiteLLM's centralized logging
|
||||
- ✅ **Seamless Model Switching**: Easily switch between Claude and Gemini models
|
||||
- ✅ **Full Feature Support**: All Gemini 3 features (thought signatures, function calling, etc.) work through Claude Code
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Claude Code not finding the model:**
|
||||
- Ensure the model name in Claude Code matches exactly: `gemini-3-pro-preview`
|
||||
- Verify your proxy is running: `curl http://0.0.0.0:4000/health`
|
||||
- Check that `ANTHROPIC_BASE_URL` points to your LiteLLM proxy
|
||||
|
||||
**Authentication errors:**
|
||||
- Verify `ANTHROPIC_AUTH_TOKEN` matches your LiteLLM master key
|
||||
- Ensure `GEMINI_API_KEY` is set correctly
|
||||
- Check LiteLLM proxy logs for detailed error messages
|
||||
|
||||
## Responses API Support
|
||||
|
||||
LiteLLM fully supports the OpenAI Responses API for Gemini 3 Pro Preview, including both streaming and non-streaming modes. The Responses API provides a structured way to handle multi-turn conversations with function calling, and LiteLLM automatically preserves thought signatures throughout the conversation.
|
||||
|
||||
### Example: Using Responses API with Gemini 3
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="Non-Streaming">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
# 1. Define a list of callable tools for the model
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_horoscope",
|
||||
"description": "Get today's horoscope for an astrological sign.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sign": {
|
||||
"type": "string",
|
||||
"description": "An astrological sign like Taurus or Aquarius",
|
||||
},
|
||||
},
|
||||
"required": ["sign"],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def get_horoscope(sign):
|
||||
return f"{sign}: Next Tuesday you will befriend a baby otter."
|
||||
|
||||
# Create a running input list we will add to over time
|
||||
input_list = [
|
||||
{"role": "user", "content": "What is my horoscope? I am an Aquarius."}
|
||||
]
|
||||
|
||||
# 2. Prompt the model with tools defined
|
||||
response = client.responses.create(
|
||||
model="gemini-3-pro-preview",
|
||||
tools=tools,
|
||||
input=input_list,
|
||||
)
|
||||
|
||||
# Save function call outputs for subsequent requests
|
||||
input_list += response.output
|
||||
|
||||
for item in response.output:
|
||||
if item.type == "function_call":
|
||||
if item.name == "get_horoscope":
|
||||
# 3. Execute the function logic for get_horoscope
|
||||
horoscope = get_horoscope(json.loads(item.arguments))
|
||||
|
||||
# 4. Provide function call results to the model
|
||||
input_list.append({
|
||||
"type": "function_call_output",
|
||||
"call_id": item.call_id,
|
||||
"output": json.dumps({
|
||||
"horoscope": horoscope
|
||||
})
|
||||
})
|
||||
|
||||
print("Final input:")
|
||||
print(input_list)
|
||||
|
||||
response = client.responses.create(
|
||||
model="gemini-3-pro-preview",
|
||||
instructions="Respond only with a horoscope generated by a tool.",
|
||||
tools=tools,
|
||||
input=input_list,
|
||||
)
|
||||
|
||||
# 5. The model should be able to give a response!
|
||||
print("Final output:")
|
||||
print(response.model_dump_json(indent=2))
|
||||
print("\n" + response.output_text)
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- ✅ Thought signatures are automatically preserved in function calls
|
||||
- ✅ Works seamlessly with multi-turn conversations
|
||||
- ✅ All Gemini 3-specific features are fully supported
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="streaming" label="Streaming">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
client = OpenAI()
|
||||
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_horoscope",
|
||||
"description": "Get today's horoscope for an astrological sign.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sign": {
|
||||
"type": "string",
|
||||
"description": "An astrological sign like Taurus or Aquarius",
|
||||
},
|
||||
},
|
||||
"required": ["sign"],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
def get_horoscope(sign):
|
||||
return f"{sign}: Next Tuesday you will befriend a baby otter."
|
||||
|
||||
input_list = [
|
||||
{"role": "user", "content": "What is my horoscope? I am an Aquarius."}
|
||||
]
|
||||
|
||||
# Streaming mode
|
||||
response = client.responses.create(
|
||||
model="gemini-3-pro-preview",
|
||||
tools=tools,
|
||||
input=input_list,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
# Collect all chunks
|
||||
chunks = []
|
||||
for chunk in response:
|
||||
chunks.append(chunk)
|
||||
# Process streaming chunks as they arrive
|
||||
print(chunk)
|
||||
|
||||
# Thought signatures are automatically preserved in streaming mode
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- ✅ Streaming mode fully supported
|
||||
- ✅ Thought signatures preserved across streaming chunks
|
||||
- ✅ Real-time processing of function calls and responses
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Responses API Benefits
|
||||
|
||||
- ✅ **Structured Output**: Responses API provides a clear structure for handling function calls and multi-turn conversations
|
||||
- ✅ **Thought Signature Preservation**: LiteLLM automatically preserves thought signatures in both streaming and non-streaming modes
|
||||
- ✅ **Seamless Integration**: Works with existing OpenAI SDK patterns
|
||||
- ✅ **Full Feature Support**: All Gemini 3 features (thought signatures, function calling, reasoning) are fully supported
|
||||
|
||||
|
||||
## Best Practices
|
||||
|
||||
#### 1. Always Include Thought Signatures in Conversation History
|
||||
|
||||
When building multi-turn conversations with function calling:
|
||||
|
||||
✅ **Do:**
|
||||
```python
|
||||
# Append the full assistant message (includes thought signatures)
|
||||
messages.append(response.choices[0].message)
|
||||
```
|
||||
|
||||
❌ **Don't:**
|
||||
```python
|
||||
# Don't manually construct assistant messages without thought signatures
|
||||
messages.append({
|
||||
"role": "assistant",
|
||||
"tool_calls": [...] # Missing thought signatures!
|
||||
})
|
||||
```
|
||||
|
||||
#### 2. Use Appropriate Thinking Levels
|
||||
|
||||
- **`reasoning_effort="low"`**: For simple queries, quick responses, cost optimization
|
||||
- **`reasoning_effort="high"`**: For complex problems requiring deep reasoning
|
||||
|
||||
#### 3. Keep Temperature at Default
|
||||
|
||||
For Gemini 3 models, always use `temperature=1.0` (default). Lower temperatures can cause issues.
|
||||
|
||||
#### 4. Handle Model Switches Gracefully
|
||||
|
||||
When switching from non-Gemini-3 to Gemini-3:
|
||||
- ✅ LiteLLM automatically handles missing thought signatures
|
||||
- ✅ No manual intervention needed
|
||||
- ✅ Conversation history continues seamlessly
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
#### Issue: Missing Thought Signatures
|
||||
|
||||
**Symptom**: Error when including assistant messages in conversation history
|
||||
|
||||
**Solution**: Ensure you're appending the full assistant message from the response:
|
||||
```python
|
||||
messages.append(response.choices[0].message) # ✅ Includes thought signatures
|
||||
```
|
||||
|
||||
#### Issue: Conversation Breaks When Switching Models
|
||||
|
||||
**Symptom**: Errors when switching from gemini-2.5-flash to gemini-3-pro-preview
|
||||
|
||||
**Solution**: This should work automatically! LiteLLM adds dummy signatures. If you see errors, ensure you're using the latest LiteLLM version.
|
||||
|
||||
#### Issue: Infinite Loops or Poor Performance
|
||||
|
||||
**Symptom**: Model gets stuck or produces poor results
|
||||
|
||||
**Solution**:
|
||||
- Ensure `temperature=1.0` (default for Gemini 3)
|
||||
- Check that `reasoning_effort` is set appropriately
|
||||
- Verify you're using the correct model name: `gemini/gemini-3-pro-preview`
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Gemini Provider Documentation](../gemini.md)
|
||||
- [Thought Signatures Guide](../gemini.md#thought-signatures)
|
||||
- [Reasoning Content Documentation](../../reasoning_content.md)
|
||||
- [Function Calling Guide](../../function_calling.md)
|
||||
|
||||
|
|
@ -37,57 +37,7 @@ Two files: `my_guardrail.py` (main class) and `__init__.py` (initialization).
|
|||
|
||||
`my_guardrail.py`:
|
||||
|
||||
```python
|
||||
import os
|
||||
from typing import Optional, List
|
||||
from fastapi import HTTPException
|
||||
|
||||
from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.types.guardrails import PiiEntityType
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.llms.custom_httpx.http_handler import (
|
||||
get_async_httpx_client,
|
||||
httpxSpecialProvider,
|
||||
)
|
||||
|
||||
class MyGuardrail(CustomGuardrail):
|
||||
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs):
|
||||
self.api_key = api_key or os.getenv("MY_GUARDRAIL_API_KEY")
|
||||
self.api_base = api_base or os.getenv("MY_GUARDRAIL_API_BASE", "https://api.myguardrail.com")
|
||||
super().__init__(default_on=True)
|
||||
|
||||
async def apply_guardrail(
|
||||
self,
|
||||
text: str,
|
||||
language: Optional[str] = None,
|
||||
entities: Optional[List[PiiEntityType]] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> str:
|
||||
result = await self._check_with_api(text, request_data)
|
||||
|
||||
if result.get("action") == "BLOCK":
|
||||
raise Exception(f"Content blocked: {result.get('reason', 'Policy violation')}")
|
||||
|
||||
return text
|
||||
|
||||
async def _check_with_api(self, text: str, request_data: Optional[dict]) -> dict:
|
||||
async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
}
|
||||
|
||||
response = await async_client.post(
|
||||
f"{self.api_base}/check",
|
||||
headers=headers,
|
||||
json={"text": text},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
```
|
||||
Follow from [Custom Guardrail](../proxy/guardrails/custom_guardrail#custom-guardrail) tutorial.
|
||||
|
||||
### Create the Init File
|
||||
|
||||
|
|
|
|||
|
|
@ -174,6 +174,257 @@ print("list_batches_response=", list_batches_response)
|
|||
</Tabs>
|
||||
|
||||
|
||||
## Multi-Account / Model-Based Routing
|
||||
|
||||
Route batch operations to different provider accounts using model-specific credentials from your `config.yaml`. This eliminates the need for environment variables and enables multi-tenant batch processing.
|
||||
|
||||
### How It Works
|
||||
|
||||
**Priority Order:**
|
||||
1. **Encoded Batch/File ID** (highest) - Model info embedded in the ID
|
||||
2. **Model Parameter** - Via header (`x-litellm-model`), query param, or request body
|
||||
3. **Custom Provider** (fallback) - Uses environment variables
|
||||
|
||||
### Configuration
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4o-account-1
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: sk-account-1-key
|
||||
api_base: https://api.openai.com/v1
|
||||
|
||||
- model_name: gpt-4o-account-2
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: sk-account-2-key
|
||||
api_base: https://api.openai.com/v1
|
||||
|
||||
- model_name: azure-batches
|
||||
litellm_params:
|
||||
model: azure/gpt-4
|
||||
api_key: azure-key-123
|
||||
api_base: https://my-resource.openai.azure.com
|
||||
api_version: "2024-02-01"
|
||||
```
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Scenario 1: Encoded File ID with Model
|
||||
|
||||
When you upload a file with a model parameter, LiteLLM encodes the model information in the file ID. All subsequent operations automatically use those credentials.
|
||||
|
||||
```bash
|
||||
# Step 1: Upload file with model
|
||||
curl http://localhost:4000/v1/files \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "x-litellm-model: gpt-4o-account-1" \
|
||||
-F purpose="batch" \
|
||||
-F file="@batch.jsonl"
|
||||
|
||||
# Response includes encoded file ID:
|
||||
# {
|
||||
# "id": "file-bGl0ZWxsbTpmaWxlLUxkaUwzaVYxNGZRVlpYcU5KVEdkSjk7bW9kZWwsZ3B0LTRvLWFjY291bnQtMQ",
|
||||
# ...
|
||||
# }
|
||||
|
||||
# Step 2: Create batch - automatically routes to gpt-4o-account-1
|
||||
curl http://localhost:4000/v1/batches \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-bGl0ZWxsbTpmaWxlLUxkaUwzaVYxNGZRVlpYcU5KVEdkSjk7bW9kZWwsZ3B0LTRvLWFjY291bnQtMQ",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
|
||||
# Batch ID is also encoded with model:
|
||||
# {
|
||||
# "id": "batch_bGl0ZWxsbTpiYXRjaF82OTIwM2IzNjg0MDQ4MTkwYTA3ODQ5NDY3YTFjMDJkYTttb2RlbCxncHQtNG8tYWNjb3VudC0x",
|
||||
# "input_file_id": "file-bGl0ZWxsbTpmaWxlLUxkaUwzaVYxNGZRVlpYcU5KVEdkSjk7bW9kZWwsZ3B0LTRvLWFjY291bnQtMQ",
|
||||
# ...
|
||||
# }
|
||||
|
||||
# Step 3: Retrieve batch - automatically routes to gpt-4o-account-1
|
||||
curl http://localhost:4000/v1/batches/batch_bGl0ZWxsbTpiYXRjaF82OTIwM2IzNjg0MDQ4MTkwYTA3ODQ5NDY3YTFjMDJkYTttb2RlbCxncHQtNG8tYWNjb3VudC0x \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
```
|
||||
|
||||
**✅ Benefits:**
|
||||
- No need to specify model on every request
|
||||
- File and batch IDs "remember" which account created them
|
||||
- Automatic routing for retrieve, cancel, and file content operations
|
||||
|
||||
#### Scenario 2: Model via Header/Query Parameter
|
||||
|
||||
Specify the model for each request without encoding it in the ID.
|
||||
|
||||
```bash
|
||||
# Create batch with model header
|
||||
curl http://localhost:4000/v1/batches \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "x-litellm-model: gpt-4o-account-2" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-abc123",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
|
||||
# Or use query parameter
|
||||
curl "http://localhost:4000/v1/batches?model=gpt-4o-account-2" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-abc123",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
|
||||
# List batches for specific model
|
||||
curl "http://localhost:4000/v1/batches?model=gpt-4o-account-2" \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
```
|
||||
|
||||
**✅ Use Case:**
|
||||
- One-off batch operations
|
||||
- Different models for different operations
|
||||
- Explicit control over routing
|
||||
|
||||
#### Scenario 3: Environment Variables (Fallback)
|
||||
|
||||
Traditional approach using environment variables when no model is specified.
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY="sk-env-key"
|
||||
|
||||
curl http://localhost:4000/v1/batches \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"input_file_id": "file-abc123",
|
||||
"endpoint": "/v1/chat/completions",
|
||||
"completion_window": "24h"
|
||||
}'
|
||||
```
|
||||
|
||||
**✅ Use Case:**
|
||||
- Backward compatibility
|
||||
- Simple single-account setups
|
||||
- Quick prototyping
|
||||
|
||||
### Complete Multi-Account Example
|
||||
|
||||
```bash
|
||||
# Upload file to Account 1
|
||||
FILE_1=$(curl -s http://localhost:4000/v1/files \
|
||||
-H "x-litellm-model: gpt-4o-account-1" \
|
||||
-F purpose="batch" \
|
||||
-F file="@batch1.jsonl" | jq -r '.id')
|
||||
|
||||
# Upload file to Account 2
|
||||
FILE_2=$(curl -s http://localhost:4000/v1/files \
|
||||
-H "x-litellm-model: gpt-4o-account-2" \
|
||||
-F purpose="batch" \
|
||||
-F file="@batch2.jsonl" | jq -r '.id')
|
||||
|
||||
# Create batch on Account 1 (auto-routed via encoded file ID)
|
||||
BATCH_1=$(curl -s http://localhost:4000/v1/batches \
|
||||
-d "{\"input_file_id\": \"$FILE_1\", \"endpoint\": \"/v1/chat/completions\", \"completion_window\": \"24h\"}" | jq -r '.id')
|
||||
|
||||
# Create batch on Account 2 (auto-routed via encoded file ID)
|
||||
BATCH_2=$(curl -s http://localhost:4000/v1/batches \
|
||||
-d "{\"input_file_id\": \"$FILE_2\", \"endpoint\": \"/v1/chat/completions\", \"completion_window\": \"24h\"}" | jq -r '.id')
|
||||
|
||||
# Retrieve both batches (auto-routed to correct accounts)
|
||||
curl http://localhost:4000/v1/batches/$BATCH_1
|
||||
curl http://localhost:4000/v1/batches/$BATCH_2
|
||||
|
||||
# List batches per account
|
||||
curl "http://localhost:4000/v1/batches?model=gpt-4o-account-1"
|
||||
curl "http://localhost:4000/v1/batches?model=gpt-4o-account-2"
|
||||
```
|
||||
|
||||
### SDK Usage with Model Routing
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import asyncio
|
||||
|
||||
# Upload file with model routing
|
||||
file_obj = await litellm.acreate_file(
|
||||
file=open("batch.jsonl", "rb"),
|
||||
purpose="batch",
|
||||
model="gpt-4o-account-1", # Route to specific account
|
||||
)
|
||||
|
||||
print(f"File ID: {file_obj.id}")
|
||||
# File ID is encoded with model info
|
||||
|
||||
# Create batch - automatically uses gpt-4o-account-1 credentials
|
||||
batch = await litellm.acreate_batch(
|
||||
completion_window="24h",
|
||||
endpoint="/v1/chat/completions",
|
||||
input_file_id=file_obj.id, # Model info embedded in ID
|
||||
)
|
||||
|
||||
print(f"Batch ID: {batch.id}")
|
||||
# Batch ID is also encoded
|
||||
|
||||
# Retrieve batch - automatically routes to correct account
|
||||
retrieved = await litellm.aretrieve_batch(
|
||||
batch_id=batch.id, # Model info embedded in ID
|
||||
)
|
||||
|
||||
print(f"Batch status: {retrieved.status}")
|
||||
|
||||
# Or explicitly specify model
|
||||
batch2 = await litellm.acreate_batch(
|
||||
completion_window="24h",
|
||||
endpoint="/v1/chat/completions",
|
||||
input_file_id="file-regular-id",
|
||||
model="gpt-4o-account-2", # Explicit routing
|
||||
)
|
||||
```
|
||||
|
||||
### How ID Encoding Works
|
||||
|
||||
LiteLLM encodes model information into file and batch IDs using base64:
|
||||
|
||||
```
|
||||
Original: file-abc123
|
||||
Encoded: file-bGl0ZWxsbTpmaWxlLWFiYzEyMzttb2RlbCxncHQtNG8tdGVzdA
|
||||
└─┬─┘ └──────────────────┬──────────────────────┘
|
||||
prefix base64(litellm:file-abc123;model,gpt-4o-test)
|
||||
|
||||
Original: batch_xyz789
|
||||
Encoded: batch_bGl0ZWxsbTpiYXRjaF94eXo3ODk7bW9kZWwsZ3B0LTRvLXRlc3Q
|
||||
└──┬──┘ └──────────────────┬──────────────────────┘
|
||||
prefix base64(litellm:batch_xyz789;model,gpt-4o-test)
|
||||
```
|
||||
|
||||
The encoding:
|
||||
- ✅ Preserves OpenAI-compatible prefixes (`file-`, `batch_`)
|
||||
- ✅ Is transparent to clients
|
||||
- ✅ Enables automatic routing without additional parameters
|
||||
- ✅ Works across all batch and file endpoints
|
||||
|
||||
### Supported Endpoints
|
||||
|
||||
All batch and file endpoints support model-based routing:
|
||||
|
||||
| Endpoint | Method | Model Routing |
|
||||
|----------|--------|---------------|
|
||||
| `/v1/files` | POST | ✅ Via header/query/body |
|
||||
| `/v1/files/{file_id}` | GET | ✅ Auto from encoded ID + header/query |
|
||||
| `/v1/files/{file_id}/content` | GET | ✅ Auto from encoded ID + header/query |
|
||||
| `/v1/files/{file_id}` | DELETE | ✅ Auto from encoded ID |
|
||||
| `/v1/batches` | POST | ✅ Auto from file ID + header/query/body |
|
||||
| `/v1/batches` | GET | ✅ Via header/query |
|
||||
| `/v1/batches/{batch_id}` | GET | ✅ Auto from encoded ID |
|
||||
| `/v1/batches/{batch_id}/cancel` | POST | ✅ Auto from encoded ID |
|
||||
|
||||
## **Supported Providers**:
|
||||
### [Azure OpenAI](./providers/azure#azure-batches-api)
|
||||
### [OpenAI](#quick-start)
|
||||
|
|
|
|||
|
|
@ -125,18 +125,23 @@ class MyUser(HttpUser):
|
|||
## LiteLLM vs Portkey Performance Comparison
|
||||
|
||||
**Test Configuration**: 4 CPUs, 8 GB RAM per instance | Load: 1k concurrent users, 500 ramp-up
|
||||
**Versions:** Portkey **v1.14.0** | LiteLLM **v1.79.1-stable**
|
||||
**Test Duration:** 5 minutes
|
||||
|
||||
### Multi-Instance (4×) Performance
|
||||
|
||||
| Metric | Portkey (no DB) | LiteLLM (with DB) |
|
||||
| ------------------- | --------------- | ----------------- |
|
||||
| **Total Requests** | 293,796 | 312,405 |
|
||||
| **Failed Requests** | 0 | 0 |
|
||||
| **Median Latency** | 100 ms | 100 ms |
|
||||
| **p95 Latency** | 230 ms | 150 ms |
|
||||
| **p99 Latency** | 500 ms | 240 ms |
|
||||
| **Average Latency** | 123 ms | 111 ms |
|
||||
| **Current RPS** | 1,170.9 | 1,170 |
|
||||
| Metric | Portkey (no DB) | LiteLLM (with DB) | Comment |
|
||||
| ------------------- | --------------- | ----------------- | -------------- |
|
||||
| **Total Requests** | 293,796 | 312,405 | LiteLLM higher |
|
||||
| **Failed Requests** | 0 | 0 | Same |
|
||||
| **Median Latency** | 100 ms | 100 ms | Same |
|
||||
| **p95 Latency** | 230 ms | 150 ms | LiteLLM lower |
|
||||
| **p99 Latency** | 500 ms | 240 ms | LiteLLM lower |
|
||||
| **Average Latency** | 123 ms | 111 ms | LiteLLM lower |
|
||||
| **Current RPS** | 1,170.9 | 1,170 | Same |
|
||||
|
||||
|
||||
*Lower is better for latency metrics; higher is better for requests and RPS.*
|
||||
|
||||
### Technical Insights
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ response = completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -92,7 +92,7 @@ response = client.chat.completions.create(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -230,7 +230,7 @@ response = completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png",
|
||||
"format": "image/jpeg"
|
||||
}
|
||||
}
|
||||
|
|
@ -292,7 +292,7 @@ response = client.chat.completions.create(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png",
|
||||
"format": "image/jpeg"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import Image from '@theme/IdealImage';
|
|||
# Enterprise
|
||||
|
||||
:::info
|
||||
✨ SSO is free for up to 5 users. After that, an enterprise license is required. [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
- ✨ SSO is free for up to 5 users. After that, an enterprise license is required. [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
- Who is Enterprise for? Companies giving access to 100+ users **OR** 10+ AI use-cases. If you're not sure, [get in touch with us](https://calendly.com/d/4mp-gd3-k5k/litellm-1-1-onboarding-chat) to discuss your needs.
|
||||
:::
|
||||
|
||||
For companies that need SSO, user management and professional support for LiteLLM Proxy
|
||||
|
|
|
|||
|
|
@ -107,3 +107,18 @@ docker run \
|
|||
litellm_test_image \
|
||||
--config /app/config.yaml --detailed_debug
|
||||
```
|
||||
### Running LiteLLM Proxy Locally
|
||||
|
||||
1. cd into the `proxy/` directory
|
||||
|
||||
```
|
||||
cd litellm/litellm/proxy
|
||||
```
|
||||
|
||||
2. Run the proxy
|
||||
|
||||
```shell
|
||||
python3 proxy_cli.py --config /path/to/config.yaml
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
|
@ -16,7 +16,137 @@ Use this to call the provider's `/files` endpoints directly, in the OpenAI forma
|
|||
- Delete File
|
||||
- Get File Content
|
||||
|
||||
## Multi-Account Support (Multiple OpenAI Keys)
|
||||
|
||||
Use different OpenAI API keys for files and batches by specifying a `model` parameter that references entries in your `model_list`. This approach works **without requiring a database** and allows you to route files/batches to different OpenAI accounts.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Define models in `model_list` with different API keys
|
||||
2. Pass `model` parameter when creating files
|
||||
3. LiteLLM returns encoded IDs that contain routing information
|
||||
4. Use encoded IDs for all subsequent operations (retrieve, delete, batches)
|
||||
5. No need to specify model again - routing info is in the ID
|
||||
|
||||
### Setup
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
# litellm OpenAI Account
|
||||
- model_name: "gpt-4o-litellm"
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: os.environ/OPENAI_LITELLM_API_KEY
|
||||
|
||||
# Free OpenAI Account
|
||||
- model_name: "gpt-4o-free"
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: os.environ/OPENAI_FREE_API_KEY
|
||||
```
|
||||
|
||||
### Usage Example
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key="sk-1234", # Your LiteLLM proxy key
|
||||
base_url="http://0.0.0.0:4000"
|
||||
)
|
||||
|
||||
# Create file using litellm account
|
||||
file_response = client.files.create(
|
||||
file=open("batch_data.jsonl", "rb"),
|
||||
purpose="batch",
|
||||
extra_body={"model": "gpt-4o-litellm"} # Routes to litellm key
|
||||
)
|
||||
print(f"File ID: {file_response.id}")
|
||||
# Returns encoded ID like: file-bGl0ZWxsbTpmaWxlLWFiYzEyMzttb2RlbCxncHQtNG8taWZvb2Q
|
||||
|
||||
# Create batch using the encoded file ID
|
||||
# No need to specify model again - it's embedded in the file ID
|
||||
batch_response = client.batches.create(
|
||||
input_file_id=file_response.id, # Encoded ID
|
||||
endpoint="/v1/chat/completions",
|
||||
completion_window="24h"
|
||||
)
|
||||
print(f"Batch ID: {batch_response.id}")
|
||||
# Returns encoded batch ID with routing info
|
||||
|
||||
# Retrieve batch - routing happens automatically
|
||||
batch_status = client.batches.retrieve(batch_response.id)
|
||||
print(f"Status: {batch_status.status}")
|
||||
|
||||
# List files for a specific account
|
||||
files = client.files.list(
|
||||
extra_body={"model": "gpt-4o-free"} # List free files
|
||||
)
|
||||
|
||||
# List batches for a specific account
|
||||
batches = client.batches.list(
|
||||
extra_query={"model": "gpt-4o-litellm"} # List litellm batches
|
||||
)
|
||||
```
|
||||
|
||||
### Parameter Options
|
||||
|
||||
You can pass the `model` parameter via:
|
||||
- **Request body**: `extra_body={"model": "gpt-4o-litellm"}`
|
||||
- **Query parameter**: `?model=gpt-4o-litellm`
|
||||
- **Header**: `x-litellm-model: gpt-4o-litellm`
|
||||
|
||||
### How Encoded IDs Work
|
||||
|
||||
- When you create a file/batch with a `model` parameter, LiteLLM encodes the model name into the returned ID
|
||||
- The encoded ID is base64-encoded and looks like: `file-bGl0ZWxsbTpmaWxlLWFiYzEyMzttb2RlbCxncHQtNG8taWZvb2Q`
|
||||
- When you use this ID in subsequent operations (retrieve, delete, batch create), LiteLLM automatically:
|
||||
1. Decodes the ID
|
||||
2. Extracts the model name
|
||||
3. Looks up the credentials
|
||||
4. Routes the request to the correct OpenAI account
|
||||
- The original provider file/batch ID is preserved internally
|
||||
|
||||
### Benefits
|
||||
|
||||
✅ **No Database Required** - All routing info stored in the ID
|
||||
✅ **Stateless** - Works across proxy restarts
|
||||
✅ **Simple** - Just pass the ID around like normal
|
||||
✅ **Backward Compatible** - Existing `custom_llm_provider` and `files_settings` still work
|
||||
✅ **Future-Proof** - Aligns with managed batches approach
|
||||
|
||||
### Migration from files_settings
|
||||
|
||||
**Old approach (still works):**
|
||||
```yaml
|
||||
files_settings:
|
||||
- custom_llm_provider: openai
|
||||
api_key: os.environ/OPENAI_KEY
|
||||
```
|
||||
|
||||
```python
|
||||
# Had to specify provider on every call
|
||||
client.files.create(..., extra_headers={"custom-llm-provider": "openai"})
|
||||
client.files.retrieve(file_id, extra_headers={"custom-llm-provider": "openai"})
|
||||
```
|
||||
|
||||
**New approach (recommended):**
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: "gpt-4o-account1"
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: os.environ/OPENAI_KEY
|
||||
```
|
||||
|
||||
```python
|
||||
# Specify model once on create
|
||||
file = client.files.create(..., extra_body={"model": "gpt-4o-account1"})
|
||||
|
||||
# Then just use the ID - routing is automatic
|
||||
client.files.retrieve(file.id) # No need to specify account
|
||||
client.batches.create(input_file_id=file.id) # Routes correctly
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="proxy" label="LiteLLM PROXY Server">
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ LiteLLM provides image editing functionality that maps to OpenAI's `/images/edit
|
|||
| Supported operations | Create image edits | Single and multiple images supported |
|
||||
| Supported LiteLLM SDK Versions | 1.63.8+ | Gemini support requires 1.79.3+ |
|
||||
| Supported LiteLLM Proxy Versions | 1.71.1+ | Gemini support requires 1.79.3+ |
|
||||
| Supported LLM providers | **OpenAI**, **Gemini (Google AI Studio)** | Gemini supports the new `gemini-2.5-flash-image` family |
|
||||
| Supported LLM providers | **OpenAI**, **Gemini (Google AI Studio)**, **Vertex AI** | Gemini supports the new `gemini-2.5-flash-image` family. Vertex AI supports both Gemini and Imagen models. |
|
||||
|
||||
#### ⚡️See all supported models and providers at [models.litellm.ai](https://models.litellm.ai/)
|
||||
|
||||
|
|
@ -197,6 +197,53 @@ for idx, image_obj in enumerate(response.data):
|
|||
f.write(base64.b64decode(image_obj.b64_json))
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="vertex_ai" label="Vertex AI">
|
||||
|
||||
#### Basic Image Edit (Gemini)
|
||||
```python showLineNumbers title="Vertex AI Gemini Image Edit"
|
||||
import os
|
||||
import litellm
|
||||
|
||||
# Set Vertex AI credentials
|
||||
os.environ["VERTEXAI_PROJECT"] = "your-gcp-project-id"
|
||||
os.environ["VERTEXAI_LOCATION"] = "us-central1"
|
||||
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/service-account.json"
|
||||
|
||||
response = litellm.image_edit(
|
||||
model="vertex_ai/gemini-2.5-flash",
|
||||
image=open("original_image.png", "rb"),
|
||||
prompt="Add neon lights in the background",
|
||||
size="1024x1024",
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
#### Image Edit with Imagen (Supports Masks)
|
||||
```python showLineNumbers title="Vertex AI Imagen Image Edit"
|
||||
import os
|
||||
import litellm
|
||||
|
||||
# Set Vertex AI credentials
|
||||
os.environ["VERTEXAI_PROJECT"] = "your-gcp-project-id"
|
||||
os.environ["VERTEXAI_LOCATION"] = "us-central1"
|
||||
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/path/to/service-account.json"
|
||||
|
||||
# Imagen supports mask for inpainting
|
||||
response = litellm.image_edit(
|
||||
model="vertex_ai/imagen-3.0-capability-001",
|
||||
image=open("original_image.png", "rb"),
|
||||
mask=open("mask_image.png", "rb"), # Optional: for inpainting
|
||||
prompt="Turn this into watercolor style scenery",
|
||||
n=2, # Number of variations
|
||||
size="1024x1024",
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -302,6 +349,55 @@ curl -X POST "http://0.0.0.0:4000/v1/images/edits" \
|
|||
-F "size=1024x1024"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="vertex_ai" label="Vertex AI">
|
||||
|
||||
1. Add Vertex AI image edit models to your `config.yaml`:
|
||||
```yaml showLineNumbers title="Vertex AI Proxy Configuration"
|
||||
model_list:
|
||||
- model_name: vertex-gemini-image-edit
|
||||
litellm_params:
|
||||
model: vertex_ai/gemini-2.5-flash
|
||||
vertex_project: os.environ/VERTEXAI_PROJECT
|
||||
vertex_location: os.environ/VERTEXAI_LOCATION
|
||||
vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS
|
||||
|
||||
- model_name: vertex-imagen-image-edit
|
||||
litellm_params:
|
||||
model: vertex_ai/imagen-3.0-capability-001
|
||||
vertex_project: os.environ/VERTEXAI_PROJECT
|
||||
vertex_location: os.environ/VERTEXAI_LOCATION
|
||||
vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS
|
||||
```
|
||||
|
||||
2. Start the LiteLLM proxy server:
|
||||
```bash showLineNumbers title="Start LiteLLM Proxy Server"
|
||||
litellm --config /path/to/config.yaml
|
||||
```
|
||||
|
||||
3. Make an image edit request:
|
||||
```bash showLineNumbers title="Vertex AI Gemini Proxy Image Edit"
|
||||
curl -X POST "http://0.0.0.0:4000/v1/images/edits" \
|
||||
-H "Authorization: Bearer <YOUR-LITELLM-KEY>" \
|
||||
-F "model=vertex-gemini-image-edit" \
|
||||
-F "image=@original_image.png" \
|
||||
-F "prompt=Add neon lights in the background" \
|
||||
-F "size=1024x1024"
|
||||
```
|
||||
|
||||
4. Imagen image edit with mask:
|
||||
```bash showLineNumbers title="Vertex AI Imagen Proxy Image Edit with Mask"
|
||||
curl -X POST "http://0.0.0.0:4000/v1/images/edits" \
|
||||
-H "Authorization: Bearer <YOUR-LITELLM-KEY>" \
|
||||
-F "model=vertex-imagen-image-edit" \
|
||||
-F "image=@original_image.png" \
|
||||
-F "mask=@mask_image.png" \
|
||||
-F "prompt=Turn this into watercolor style scenery" \
|
||||
-F "n=2" \
|
||||
-F "size=1024x1024"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -657,7 +657,7 @@ LiteLLM Proxy provides two methods for controlling access to specific MCP server
|
|||
|
||||
### Method 1: URL-based Namespacing
|
||||
|
||||
LiteLLM Proxy supports URL-based namespacing for MCP servers using the format `/mcp/<servers or access groups>`. This allows you to:
|
||||
LiteLLM Proxy supports URL-based namespacing for MCP servers using the format `/<servers or access groups>/mcp`. This allows you to:
|
||||
|
||||
- **Direct URL Access**: Point MCP clients directly to specific servers or access groups via URL
|
||||
- **Simplified Configuration**: Use URLs instead of headers for server selection
|
||||
|
|
@ -666,14 +666,14 @@ LiteLLM Proxy supports URL-based namespacing for MCP servers using the format `/
|
|||
#### URL Format
|
||||
|
||||
```
|
||||
<your-litellm-proxy-base-url>/mcp/<server_alias_or_access_group>
|
||||
<your-litellm-proxy-base-url>/<server_alias_or_access_group>/mcp
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- `/mcp/github` - Access tools from the "github" MCP server
|
||||
- `/mcp/zapier` - Access tools from the "zapier" MCP server
|
||||
- `/mcp/dev_group` - Access tools from all servers in the "dev_group" access group
|
||||
- `/mcp/github,zapier` - Access tools from multiple specific servers
|
||||
- `/github_mcp/mcp` - Access tools from the "github_mcp" MCP server
|
||||
- `/zapier/mcp` - Access tools from the "zapier" MCP server
|
||||
- `/dev_group/mcp` - Access tools from all servers in the "dev_group" access group
|
||||
- `/github_mcp,zapier/mcp` - Access tools from multiple specific servers
|
||||
|
||||
#### Usage Examples
|
||||
|
||||
|
|
@ -690,7 +690,7 @@ curl --location 'https://api.openai.com/v1/responses' \
|
|||
{
|
||||
"type": "mcp",
|
||||
"server_label": "litellm",
|
||||
"server_url": "<your-litellm-proxy-base-url>/mcp/github",
|
||||
"server_url": "<your-litellm-proxy-base-url>/github_mcp/mcp",
|
||||
"require_approval": "never",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY"
|
||||
|
|
@ -718,7 +718,7 @@ curl --location '<your-litellm-proxy-base-url>/v1/responses' \
|
|||
{
|
||||
"type": "mcp",
|
||||
"server_label": "litellm",
|
||||
"server_url": "<your-litellm-proxy-base-url>/mcp/dev_group",
|
||||
"server_url": "<your-litellm-proxy-base-url>/dev_group/mcp",
|
||||
"require_approval": "never",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY"
|
||||
|
|
@ -740,7 +740,7 @@ This example uses URL namespacing to access all servers in the "dev_group" acces
|
|||
{
|
||||
"mcpServers": {
|
||||
"LiteLLM": {
|
||||
"url": "<your-litellm-proxy-base-url>/mcp/github,zapier",
|
||||
"url": "<your-litellm-proxy-base-url>/github_mcp,zapier/mcp",
|
||||
"headers": {
|
||||
"x-litellm-api-key": "Bearer $LITELLM_API_KEY"
|
||||
}
|
||||
|
|
@ -862,8 +862,8 @@ This configuration in Cursor IDE settings will limit tool access to only the spe
|
|||
|
||||
| Feature | Header Namespacing | URL Namespacing |
|
||||
|---------|-------------------|-----------------|
|
||||
| **Method** | Uses `x-mcp-servers` header | Uses URL path `/mcp/<servers>` |
|
||||
| **Endpoint** | Standard `litellm_proxy` endpoint | Custom `/mcp/<servers>` endpoint |
|
||||
| **Method** | Uses `x-mcp-servers` header | Uses URL path `/<servers>/mcp` |
|
||||
| **Endpoint** | Standard `litellm_proxy` endpoint | Custom `/<servers>/mcp` endpoint |
|
||||
| **Configuration** | Requires additional header | Self-contained in URL |
|
||||
| **Multiple Servers** | Comma-separated in header | Comma-separated in URL path |
|
||||
| **Access Groups** | Supported via header | Supported via URL path |
|
||||
|
|
@ -1221,7 +1221,6 @@ curl --location 'http://localhost:4000/github_mcp/mcp' \
|
|||
|
||||
LiteLLM v 1.77.6 added support for OAuth 2.0 Client Credentials for MCP servers.
|
||||
|
||||
|
||||
This configuration is currently available on the config.yaml, with UI support coming soon.
|
||||
|
||||
```yaml
|
||||
|
|
@ -1235,6 +1234,71 @@ mcp_servers:
|
|||
|
||||
[**See Claude Code Tutorial**](./tutorials/claude_responses_api#connecting-mcp-servers)
|
||||
|
||||
### How It Works
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Browser as User-Agent (Browser)
|
||||
participant Client as Client
|
||||
participant LiteLLM as LiteLLM Proxy
|
||||
participant MCP as MCP Server (Resource Server)
|
||||
participant Auth as Authorization Server
|
||||
|
||||
Note over Client,LiteLLM: Step 1 – Resource discovery
|
||||
Client->>LiteLLM: GET /.well-known/oauth-protected-resource/{mcp_server_name}/mcp
|
||||
LiteLLM->>Client: Return resource metadata
|
||||
|
||||
Note over Client,LiteLLM: Step 2 – Authorization server discovery
|
||||
Client->>LiteLLM: GET /.well-known/oauth-authorization-server/{mcp_server_name}
|
||||
LiteLLM->>Client: Return authorization server metadata
|
||||
|
||||
Note over Client,Auth: Step 3 – Dynamic client registration
|
||||
Client->>LiteLLM: POST /{mcp_server_name}/register
|
||||
LiteLLM->>Auth: Forward registration request
|
||||
Auth->>LiteLLM: Issue client credentials
|
||||
LiteLLM->>Client: Return client credentials
|
||||
|
||||
Note over Client,Browser: Step 4 – User authorization (PKCE)
|
||||
Client->>Browser: Open authorization URL + code_challenge + resource
|
||||
Browser->>Auth: Authorization request
|
||||
Note over Auth: User authorizes
|
||||
Auth->>Browser: Redirect with authorization code
|
||||
Browser->>LiteLLM: Callback to LiteLLM with code
|
||||
LiteLLM->>Browser: Redirect back with authorization code
|
||||
Browser->>Client: Callback with authorization code
|
||||
|
||||
Note over Client,Auth: Step 5 – Token exchange
|
||||
Client->>LiteLLM: Token request + code_verifier + resource
|
||||
LiteLLM->>Auth: Forward token request
|
||||
Auth->>LiteLLM: Access (and refresh) token
|
||||
LiteLLM->>Client: Return tokens
|
||||
|
||||
Note over Client,MCP: Step 6 – Authenticated MCP call
|
||||
Client->>LiteLLM: MCP request with access token + LiteLLM API key
|
||||
LiteLLM->>MCP: MCP request with Bearer token
|
||||
MCP-->>LiteLLM: MCP response
|
||||
LiteLLM-->>Client: Return MCP response
|
||||
```
|
||||
|
||||
**Participants**
|
||||
|
||||
- **Client** – The MCP-capable AI agent (e.g., Claude Code, Cursor, or another IDE/agent) that initiates OAuth discovery, authorization, and tool invocations on behalf of the user.
|
||||
- **LiteLLM Proxy** – Mediates all OAuth discovery, registration, token exchange, and MCP traffic while protecting stored credentials.
|
||||
- **Authorization Server** – Issues OAuth 2.0 tokens via dynamic client registration, PKCE authorization, and token endpoints.
|
||||
- **MCP Server (Resource Server)** – The protected MCP endpoint that receives LiteLLM’s authenticated JSON-RPC requests.
|
||||
- **User-Agent (Browser)** – Temporarily involved so the end user can grant consent during the authorization step.
|
||||
|
||||
**Flow Steps**
|
||||
|
||||
1. **Resource Discovery**: The client fetches MCP resource metadata from LiteLLM’s `.well-known/oauth-protected-resource` endpoint to understand scopes and capabilities.
|
||||
2. **Authorization Server Discovery**: The client retrieves the OAuth server metadata (token endpoint, authorization endpoint, supported PKCE methods) through LiteLLM’s `.well-known/oauth-authorization-server` endpoint.
|
||||
3. **Dynamic Client Registration**: The client registers through LiteLLM, which forwards the request to the authorization server (RFC 7591). If the provider doesn’t support dynamic registration, you can pre-store `client_id`/`client_secret` in LiteLLM (e.g., GitHub MCP) and the flow proceeds the same way.
|
||||
4. **User Authorization**: The client launches a browser session (with code challenge and resource hints). The user approves access, the authorization server sends the code through LiteLLM back to the client.
|
||||
5. **Token Exchange**: The client calls LiteLLM with the authorization code, code verifier, and resource. LiteLLM exchanges them with the authorization server and returns the issued access/refresh tokens.
|
||||
6. **MCP Invocation**: With a valid token, the client sends the MCP JSON-RPC request (plus LiteLLM API key) to LiteLLM, which forwards it to the MCP server and relays the tool response.
|
||||
|
||||
See the official [MCP Authorization Flow](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-flow-steps) for additional reference.
|
||||
|
||||
## Using your MCP with client side credentials
|
||||
|
||||
Use this if you want to pass a client side authentication token to LiteLLM to then pass to your MCP to auth to your MCP.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import os
|
|||
|
||||
os.environ["PHOENIX_API_KEY"] = "" # Necessary only using Phoenix Cloud
|
||||
os.environ["PHOENIX_COLLECTOR_HTTP_ENDPOINT"] = "" # The URL of your Phoenix OSS instance e.g. http://localhost:6006/v1/traces
|
||||
os.environ["PHOENIX_PROJECT_NAME"]="litellm" # OPTIONAL: you can configure project names, otherwise traces would go to "default" project
|
||||
|
||||
# This defaults to https://app.phoenix.arize.com/v1/traces for Phoenix Cloud
|
||||
|
||||
# LLM API Keys
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ response = completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -136,6 +136,89 @@ response = speech(
|
|||
| `wav` | riff-24khz-16bit-mono-pcm | 24kHz |
|
||||
| `pcm` | raw-24khz-16bit-mono-pcm | 24kHz |
|
||||
|
||||
## Passing Raw SSML
|
||||
|
||||
LiteLLM automatically detects when your `input` contains SSML (by checking for `<speak>` tags) and passes it through to Azure without any transformation. This gives you complete control over speech synthesis.
|
||||
|
||||
**When to use raw SSML:**
|
||||
- Using the `<lang>` element with multilingual voices to translate text (e.g., English text → Spanish speech)
|
||||
- Complex SSML structures with multiple voices or prosody changes
|
||||
- Fine-grained control over pronunciation, breaks, emphasis, and other speech features
|
||||
|
||||
### LiteLLM SDK
|
||||
|
||||
```python showLineNumbers title="Raw SSML for Multilingual Translation"
|
||||
from litellm import speech
|
||||
|
||||
# Use <lang> element to convert English text to Spanish speech
|
||||
# The <lang> element forces the output language regardless of input text language
|
||||
language_code = "es-ES"
|
||||
text = "Hello, how are you today?" # English text
|
||||
voice = "en-US-AvaMultilingualNeural"
|
||||
|
||||
ssml = f"""<speak version="1.0"
|
||||
xmlns="http://www.w3.org/2001/10/synthesis"
|
||||
xmlns:mstts="http://www.w3.org/2001/mstts"
|
||||
xml:lang="{language_code}">
|
||||
<voice name="{voice}">
|
||||
<lang xml:lang="{language_code}">{text}</lang>
|
||||
</voice>
|
||||
</speak>"""
|
||||
|
||||
response = speech(
|
||||
model="azure/speech/azure-tts",
|
||||
voice=voice,
|
||||
input=ssml, # LiteLLM auto-detects SSML and sends as-is
|
||||
api_base="https://eastus.tts.speech.microsoft.com",
|
||||
api_key=os.environ["AZURE_TTS_API_KEY"],
|
||||
)
|
||||
response.stream_to_file("speech.mp3")
|
||||
```
|
||||
|
||||
```python showLineNumbers title="Raw SSML with Complex Features"
|
||||
from litellm import speech
|
||||
|
||||
# Complex SSML with multiple prosody adjustments
|
||||
ssml = """<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis'
|
||||
xmlns:mstts='https://www.w3.org/2001/mstts' xml:lang='en-US'>
|
||||
<voice name='en-US-JennyNeural'>
|
||||
<mstts:express-as style='cheerful' styledegree='2'>
|
||||
<prosody rate='+20%' pitch='high'>
|
||||
Welcome to our service!
|
||||
</prosody>
|
||||
</mstts:express-as>
|
||||
<break time='500ms'/>
|
||||
<prosody rate='-10%'>
|
||||
How can I help you today?
|
||||
</prosody>
|
||||
</voice>
|
||||
</speak>"""
|
||||
|
||||
response = speech(
|
||||
model="azure/speech/azure-tts",
|
||||
voice="en-US-JennyNeural",
|
||||
input=ssml, # LiteLLM detects <speak> and passes through unchanged
|
||||
api_base="https://eastus.tts.speech.microsoft.com",
|
||||
api_key=os.environ["AZURE_TTS_API_KEY"],
|
||||
)
|
||||
response.stream_to_file("speech.mp3")
|
||||
```
|
||||
|
||||
### LiteLLM Proxy
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/audio/speech \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "azure-speech",
|
||||
"voice": "en-US-AvaMultilingualNeural",
|
||||
"input": "<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xmlns:mstts=\"http://www.w3.org/2001/mstts\" xml:lang=\"es-ES\"><voice name=\"en-US-AvaMultilingualNeural\"><lang xml:lang=\"es-ES\">Hello, how are you today?</lang></voice></speak>"
|
||||
}' \
|
||||
--output speech.mp3
|
||||
```
|
||||
|
||||
|
||||
## Sending Azure-Specific Params
|
||||
|
||||
Azure AI Speech supports advanced SSML features through optional parameters:
|
||||
|
|
|
|||
277
docs/my-website/docs/providers/docker_model_runner.md
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Docker Model Runner
|
||||
|
||||
## Overview
|
||||
|
||||
| Property | Details |
|
||||
|-------|-------|
|
||||
| Description | Docker Model Runner allows you to run large language models locally using Docker Desktop. |
|
||||
| Provider Route on LiteLLM | `docker_model_runner/` |
|
||||
| Link to Provider Doc | [Docker Model Runner ↗](https://docs.docker.com/ai/model-runner/) |
|
||||
| Base URL | `http://localhost:22088` |
|
||||
| Supported Operations | [`/chat/completions`](#sample-usage) |
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
https://docs.docker.com/ai/model-runner/
|
||||
|
||||
**We support ALL Docker Model Runner models, just set `docker_model_runner/` as a prefix when sending completion requests**
|
||||
|
||||
## Quick Start
|
||||
|
||||
Docker Model Runner is a Docker Desktop feature that lets you run AI models locally. It provides better performance than other local solutions while maintaining OpenAI compatibility.
|
||||
|
||||
### Installation
|
||||
|
||||
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/)
|
||||
2. Enable Docker Model Runner in Docker Desktop settings
|
||||
3. Download your preferred model through Docker Desktop
|
||||
|
||||
## Environment Variables
|
||||
|
||||
```python showLineNumbers title="Environment Variables"
|
||||
os.environ["DOCKER_MODEL_RUNNER_API_BASE"] = "http://localhost:22088/engines/llama.cpp" # Optional - defaults to this
|
||||
os.environ["DOCKER_MODEL_RUNNER_API_KEY"] = "dummy-key" # Optional - Docker Model Runner may not require auth for local instances
|
||||
```
|
||||
|
||||
**Note:**
|
||||
- Docker Model Runner typically runs locally and may not require authentication. LiteLLM will use a dummy key by default if no key is provided.
|
||||
- The API base should include the engine path (e.g., `/engines/llama.cpp`)
|
||||
|
||||
## API Base Structure
|
||||
|
||||
Docker Model Runner uses a unique URL structure:
|
||||
|
||||
```
|
||||
http://model-runner.docker.internal/engines/{engine}/v1/chat/completions
|
||||
```
|
||||
|
||||
Where `{engine}` is the engine you want to use (typically `llama.cpp`).
|
||||
|
||||
**Important:** Specify the engine in your `api_base` URL, not in the model name:
|
||||
- ✅ Correct: `api_base="http://localhost:22088/engines/llama.cpp"`, `model="docker_model_runner/llama-3.1"`
|
||||
- ❌ Incorrect: `api_base="http://localhost:22088"`, `model="docker_model_runner/llama.cpp/llama-3.1"`
|
||||
|
||||
## Usage - LiteLLM Python SDK
|
||||
|
||||
### Non-streaming
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner Non-streaming Completion"
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
# Specify the engine in the api_base URL
|
||||
os.environ["DOCKER_MODEL_RUNNER_API_BASE"] = "http://localhost:22088/engines/llama.cpp"
|
||||
|
||||
messages = [{"content": "Hello, how are you?", "role": "user"}]
|
||||
|
||||
# Docker Model Runner call
|
||||
response = completion(
|
||||
model="docker_model_runner/llama-3.1",
|
||||
messages=messages
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner Streaming Completion"
|
||||
import os
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
# Specify the engine in the api_base URL
|
||||
os.environ["DOCKER_MODEL_RUNNER_API_BASE"] = "http://localhost:22088/engines/llama.cpp"
|
||||
|
||||
messages = [{"content": "Hello, how are you?", "role": "user"}]
|
||||
|
||||
# Docker Model Runner call with streaming
|
||||
response = completion(
|
||||
model="docker_model_runner/llama-3.1",
|
||||
messages=messages,
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
### Custom API Base and Engine
|
||||
|
||||
```python showLineNumbers title="Custom API Base with Different Engine"
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"content": "Hello, how are you?", "role": "user"}]
|
||||
|
||||
# Specify the engine in the api_base URL
|
||||
# Using a different host and engine
|
||||
response = completion(
|
||||
model="docker_model_runner/llama-3.1",
|
||||
messages=messages,
|
||||
api_base="http://model-runner.docker.internal/engines/llama.cpp"
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
### Using Different Engines
|
||||
|
||||
```python showLineNumbers title="Using a Different Engine"
|
||||
import litellm
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"content": "Hello, how are you?", "role": "user"}]
|
||||
|
||||
# To use a different engine, specify it in the api_base
|
||||
# For example, if Docker Model Runner supports other engines:
|
||||
response = completion(
|
||||
model="docker_model_runner/mistral-7b",
|
||||
messages=messages,
|
||||
api_base="http://localhost:22088/engines/custom-engine"
|
||||
)
|
||||
|
||||
print(response)
|
||||
```
|
||||
|
||||
## Usage - LiteLLM Proxy
|
||||
|
||||
Add the following to your LiteLLM Proxy configuration file:
|
||||
|
||||
```yaml showLineNumbers title="config.yaml"
|
||||
model_list:
|
||||
- model_name: llama-3.1
|
||||
litellm_params:
|
||||
model: docker_model_runner/llama-3.1
|
||||
api_base: http://localhost:22088/engines/llama.cpp
|
||||
|
||||
- model_name: mistral-7b
|
||||
litellm_params:
|
||||
model: docker_model_runner/mistral-7b
|
||||
api_base: http://localhost:22088/engines/llama.cpp
|
||||
```
|
||||
|
||||
Start your LiteLLM Proxy server:
|
||||
|
||||
```bash showLineNumbers title="Start LiteLLM Proxy"
|
||||
litellm --config config.yaml
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai-sdk" label="OpenAI SDK">
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner via Proxy - Non-streaming"
|
||||
from openai import OpenAI
|
||||
|
||||
# Initialize client with your proxy URL
|
||||
client = OpenAI(
|
||||
base_url="http://localhost:4000", # Your proxy URL
|
||||
api_key="your-proxy-api-key" # Your proxy API key
|
||||
)
|
||||
|
||||
# Non-streaming response
|
||||
response = client.chat.completions.create(
|
||||
model="llama-3.1",
|
||||
messages=[{"role": "user", "content": "hello from litellm"}]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner via Proxy - Streaming"
|
||||
from openai import OpenAI
|
||||
|
||||
# Initialize client with your proxy URL
|
||||
client = OpenAI(
|
||||
base_url="http://localhost:4000", # Your proxy URL
|
||||
api_key="your-proxy-api-key" # Your proxy API key
|
||||
)
|
||||
|
||||
# Streaming response
|
||||
response = client.chat.completions.create(
|
||||
model="llama-3.1",
|
||||
messages=[{"role": "user", "content": "hello from litellm"}],
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
if chunk.choices[0].delta.content is not None:
|
||||
print(chunk.choices[0].delta.content, end="")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="litellm-sdk" label="LiteLLM SDK">
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner via Proxy - LiteLLM SDK"
|
||||
import litellm
|
||||
|
||||
# Configure LiteLLM to use your proxy
|
||||
response = litellm.completion(
|
||||
model="litellm_proxy/llama-3.1",
|
||||
messages=[{"role": "user", "content": "hello from litellm"}],
|
||||
api_base="http://localhost:4000",
|
||||
api_key="your-proxy-api-key"
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
```python showLineNumbers title="Docker Model Runner via Proxy - LiteLLM SDK Streaming"
|
||||
import litellm
|
||||
|
||||
# Configure LiteLLM to use your proxy with streaming
|
||||
response = litellm.completion(
|
||||
model="litellm_proxy/llama-3.1",
|
||||
messages=[{"role": "user", "content": "hello from litellm"}],
|
||||
api_base="http://localhost:4000",
|
||||
api_key="your-proxy-api-key",
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in response:
|
||||
if hasattr(chunk.choices[0], 'delta') and chunk.choices[0].delta.content is not None:
|
||||
print(chunk.choices[0].delta.content, end="")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="curl" label="cURL">
|
||||
|
||||
```bash showLineNumbers title="Docker Model Runner via Proxy - cURL"
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-proxy-api-key" \
|
||||
-d '{
|
||||
"model": "llama-3.1",
|
||||
"messages": [{"role": "user", "content": "hello from litellm"}]
|
||||
}'
|
||||
```
|
||||
|
||||
```bash showLineNumbers title="Docker Model Runner via Proxy - cURL Streaming"
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-proxy-api-key" \
|
||||
-d '{
|
||||
"model": "llama-3.1",
|
||||
"messages": [{"role": "user", "content": "hello from litellm"}],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
For more detailed information on using the LiteLLM Proxy, see the [LiteLLM Proxy documentation](../providers/litellm_proxy).
|
||||
|
||||
## API Reference
|
||||
|
||||
For detailed API information, see the [Docker Model Runner API Reference](https://docs.docker.com/ai/model-runner/api-reference/).
|
||||
|
||||
|
|
@ -70,7 +70,11 @@ LiteLLM translates OpenAI's `reasoning_effort` to Gemini's `thinking` parameter.
|
|||
Note: Reasoning cannot be turned off on Gemini 2.5 Pro models.
|
||||
:::
|
||||
|
||||
**Mapping**
|
||||
:::tip Gemini 3 Models
|
||||
For **Gemini 3+ models** (e.g., `gemini-3-pro-preview`), LiteLLM automatically maps `reasoning_effort` to the new `thinking_level` parameter instead of `thinking_budget`. The `thinking_level` parameter uses `"low"` or `"high"` values for better control over reasoning depth.
|
||||
:::
|
||||
|
||||
**Mapping for Gemini 2.5 and earlier models**
|
||||
|
||||
| reasoning_effort | thinking | Notes |
|
||||
| ---------------- | -------- | ----- |
|
||||
|
|
@ -80,6 +84,17 @@ Note: Reasoning cannot be turned off on Gemini 2.5 Pro models.
|
|||
| "medium" | "budget_tokens": 2048 | |
|
||||
| "high" | "budget_tokens": 4096 | |
|
||||
|
||||
**Mapping for Gemini 3+ models**
|
||||
|
||||
| reasoning_effort | thinking_level | Notes |
|
||||
| ---------------- | -------------- | ----- |
|
||||
| "minimal" | "low" | Minimizes latency and cost |
|
||||
| "low" | "low" | Best for simple instruction following or chat |
|
||||
| "medium" | "high" | Maps to high (medium not yet available) |
|
||||
| "high" | "high" | Maximizes reasoning depth |
|
||||
| "disable" | "low" | Cannot fully disable thinking in Gemini 3 |
|
||||
| "none" | "low" | Cannot fully disable thinking in Gemini 3 |
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
|
|
@ -137,6 +152,59 @@ curl http://0.0.0.0:4000/v1/chat/completions \
|
|||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Gemini 3+ Models - `thinking_level` Parameter
|
||||
|
||||
For Gemini 3+ models (e.g., `gemini-3-pro-preview`), you can use the new `thinking_level` parameter directly:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
# Use thinking_level for Gemini 3 models
|
||||
resp = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "Solve this complex math problem step by step."}],
|
||||
reasoning_effort="high", # Options: "low" or "high"
|
||||
)
|
||||
|
||||
# Low thinking level for faster, simpler tasks
|
||||
resp = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=[{"role": "user", "content": "What is the weather today?"}],
|
||||
reasoning_effort="low", # Minimizes latency and cost
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
curl http://0.0.0.0:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer <YOUR-LITELLM-KEY>" \
|
||||
-d '{
|
||||
"model": "gemini-3-pro-preview",
|
||||
"messages": [{"role": "user", "content": "Solve this complex problem."}],
|
||||
"reasoning_effort": "high"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::warning
|
||||
**Temperature Recommendation for Gemini 3 Models**
|
||||
|
||||
For Gemini 3 models, LiteLLM defaults `temperature` to `1.0` and strongly recommends keeping it at this default. Setting `temperature < 1.0` can cause:
|
||||
- Infinite loops
|
||||
- Degraded reasoning performance
|
||||
- Failure on complex tasks
|
||||
|
||||
LiteLLM will automatically set `temperature=1.0` if not specified for Gemini 3+ models.
|
||||
:::
|
||||
|
||||
**Expected Response**
|
||||
|
||||
|
|
@ -951,6 +1019,297 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
|||
|
||||
|
||||
|
||||
## Thought Signatures
|
||||
|
||||
Thought signatures are encrypted representations of the model's internal reasoning process for a given turn in a conversation. By passing thought signatures back to the model in subsequent requests, you provide it with the context of its previous thoughts, allowing it to build upon its reasoning and maintain a coherent line of inquiry.
|
||||
|
||||
Thought signatures are particularly important for multi-turn function calling scenarios where the model needs to maintain context across multiple tool invocations.
|
||||
|
||||
### How Thought Signatures Work
|
||||
|
||||
- **Function calls with signatures**: When Gemini returns a function call, it includes a `thought_signature` in the response
|
||||
- **Preservation**: LiteLLM automatically extracts and stores thought signatures in `provider_specific_fields` of tool calls
|
||||
- **Return in conversation history**: When you include the assistant's message with tool calls in subsequent requests, LiteLLM automatically preserves and returns the thought signatures to Gemini
|
||||
- **Parallel function calls**: Only the first function call in a parallel set has a thought signature
|
||||
- **Sequential function calls**: Each function call in a multi-step sequence has its own signature
|
||||
|
||||
### Enabling Thought Signatures
|
||||
|
||||
To enable thought signatures, you need to enable thinking/reasoning:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
response = completion(
|
||||
model="gemini/gemini-2.5-flash",
|
||||
messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
|
||||
tools=[...],
|
||||
reasoning_effort="low", # Enable thinking to get thought signatures
|
||||
)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
```bash
|
||||
curl http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gemini-2.5-flash",
|
||||
"messages": [{"role": "user", "content": "What'\''s the weather in Tokyo?"}],
|
||||
"tools": [...],
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Multi-Turn Function Calling with Thought Signatures
|
||||
|
||||
When building conversation history for multi-turn function calling, you must include the thought signatures from previous responses. LiteLLM handles this automatically when you append the full assistant message to your conversation history.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="OpenAI Client">
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
import json
|
||||
|
||||
client = OpenAI(api_key="sk-1234", base_url="http://localhost:4000")
|
||||
|
||||
def get_current_temperature(location: str) -> dict:
|
||||
"""Gets the current weather temperature for a given location."""
|
||||
return {"temperature": 30, "unit": "celsius"}
|
||||
|
||||
def set_thermostat_temperature(temperature: int) -> dict:
|
||||
"""Sets the thermostat to a desired temperature."""
|
||||
return {"status": "success"}
|
||||
|
||||
get_weather_declaration = {
|
||||
"name": "get_current_temperature",
|
||||
"description": "Gets the current weather temperature for a given location.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"location": {"type": "string"}},
|
||||
"required": ["location"],
|
||||
},
|
||||
}
|
||||
|
||||
set_thermostat_declaration = {
|
||||
"name": "set_thermostat_temperature",
|
||||
"description": "Sets the thermostat to a desired temperature.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {"temperature": {"type": "integer"}},
|
||||
"required": ["temperature"],
|
||||
},
|
||||
}
|
||||
|
||||
# Initial request
|
||||
messages = [
|
||||
{"role": "user", "content": "If it's too hot or too cold in London, set the thermostat to a comfortable level."}
|
||||
]
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gemini-2.5-flash",
|
||||
messages=messages,
|
||||
tools=[get_weather_declaration, set_thermostat_declaration],
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
# Append the assistant's message (includes thought signatures automatically)
|
||||
messages.append(response.choices[0].message)
|
||||
|
||||
# Execute tool calls and append results
|
||||
for tool_call in response.choices[0].message.tool_calls:
|
||||
if tool_call.function.name == "get_current_temperature":
|
||||
result = get_current_temperature(**json.loads(tool_call.function.arguments))
|
||||
messages.append({
|
||||
"role": "tool",
|
||||
"content": json.dumps(result),
|
||||
"tool_call_id": tool_call.id
|
||||
})
|
||||
|
||||
# Second request - thought signatures are automatically preserved
|
||||
response2 = client.chat.completions.create(
|
||||
model="gemini-2.5-flash",
|
||||
messages=messages,
|
||||
tools=[get_weather_declaration, set_thermostat_declaration],
|
||||
reasoning_effort="low"
|
||||
)
|
||||
|
||||
print(response2.choices[0].message.content)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="curl" label="cURL">
|
||||
|
||||
```bash
|
||||
# Step 1: Initial request
|
||||
curl --location 'http://localhost:4000/v1/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--data '{
|
||||
"model": "gemini-2.5-flash",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "If it'\''s too hot or too cold in London, set the thermostat to a comfortable level."
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_temperature",
|
||||
"description": "Gets the current weather temperature for a given location.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "set_thermostat_temperature",
|
||||
"description": "Sets the thermostat to a desired temperature.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"temperature": {"type": "integer"}
|
||||
},
|
||||
"required": ["temperature"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"tool_choice": "auto",
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
The response will include tool calls with thought signatures in `provider_specific_fields`:
|
||||
|
||||
```json
|
||||
{
|
||||
"choices": [{
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"tool_calls": [{
|
||||
"id": "call_abc123",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_temperature",
|
||||
"arguments": "{\"location\": \"London\"}"
|
||||
},
|
||||
"index": 0,
|
||||
"provider_specific_fields": {
|
||||
"thought_signature": "CpcHAdHtim9+q4rstcbvQC0ic4x1/vqQlCJWgE+UZ6dTLYGHMMBkF/AxqL5UmP6SY46uYC8t4BTFiXG5zkw6EMJ...=="
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Step 2: Follow-up request with tool response
|
||||
# Include the assistant message from Step 1 (with thought signatures in provider_specific_fields)
|
||||
curl --location 'http://localhost:4000/v1/chat/completions' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer sk-1234' \
|
||||
--data '{
|
||||
"model": "gemini-2.5-flash",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "If it'\''s too hot or too cold in London, set the thermostat to a comfortable level."
|
||||
},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_c130b9f8c2c042e9b65e39a88245",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_temperature",
|
||||
"arguments": "{\"location\": \"London\"}"
|
||||
},
|
||||
"index": 0,
|
||||
"provider_specific_fields": {
|
||||
"thought_signature": "CpcHAdHtim9+q4rstcbvQC0ic4x1/vqQlCJWgE+UZ6dTLYGHMMBkF/AxqL5UmP6SY46uYC8t4BTFiXG5zkw6EMJ...=="
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "tool",
|
||||
"content": "{\"temperature\": 30, \"unit\": \"celsius\"}",
|
||||
"tool_call_id": "call_c130b9f8c2c042e9b65e39a88245"
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_current_temperature",
|
||||
"description": "Gets the current weather temperature for a given location.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "set_thermostat_temperature",
|
||||
"description": "Sets the thermostat to a desired temperature.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"temperature": {"type": "integer"}
|
||||
},
|
||||
"required": ["temperature"]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"tool_choice": "auto",
|
||||
"reasoning_effort": "low"
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Important Notes
|
||||
|
||||
1. **Automatic Handling**: LiteLLM automatically extracts thought signatures from Gemini responses and preserves them when you include assistant messages in conversation history. You don't need to manually extract or manage them.
|
||||
|
||||
2. **Parallel Function Calls**: When the model makes parallel function calls, only the first function call will have a thought signature. Subsequent parallel calls won't have signatures.
|
||||
|
||||
3. **Sequential Function Calls**: In multi-step function calling scenarios, each step's first function call will have its own thought signature that must be preserved.
|
||||
|
||||
4. **Required for Context**: Thought signatures are essential for maintaining reasoning context across multi-turn conversations with function calling. Without them, the model may lose context of its previous reasoning.
|
||||
|
||||
5. **Format**: Thought signatures are stored in `provider_specific_fields.thought_signature` of tool calls in the response, and are automatically included when you append the assistant message to your conversation history.
|
||||
|
||||
6. **Chat Completions Clients**: With chat completions clients where you cannot control whether or not the previous assistant message is included as-is (ex langchain's ChatOpenAI), LiteLLM also preserves the thought signature by appending it to the tool call id (`call_123__thought__<thought-signature>`) and extracting it back out before sending the outbound request to Gemini.
|
||||
|
||||
## JSON Mode
|
||||
|
||||
<Tabs>
|
||||
|
|
@ -1022,6 +1381,56 @@ LiteLLM Supports the following image types passed in `url`
|
|||
- Images with direct links - https://storage.googleapis.com/github-repo/img/gemini/intro/landmark3.jpg
|
||||
- Image in local storage - ./localimage.jpeg
|
||||
|
||||
## Image Resolution Control (Gemini 3+)
|
||||
|
||||
For Gemini 3+ models, LiteLLM supports per-part media resolution control using OpenAI's `detail` parameter. This allows you to specify different resolution levels for individual images in your request.
|
||||
|
||||
**Supported `detail` values:**
|
||||
- `"low"` - Maps to `media_resolution: "low"` (280 tokens for images, 70 tokens per frame for videos)
|
||||
- `"high"` - Maps to `media_resolution: "high"` (1120 tokens for images)
|
||||
- `"auto"` or `None` - Model decides optimal resolution (no `media_resolution` set)
|
||||
|
||||
**Usage Example:**
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
messages = [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://example.com/chart.png",
|
||||
"detail": "high" # High resolution for detailed chart analysis
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Analyze this chart"
|
||||
},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://example.com/icon.png",
|
||||
"detail": "low" # Low resolution for simple icon
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
response = completion(
|
||||
model="gemini/gemini-3-pro-preview",
|
||||
messages=messages,
|
||||
)
|
||||
```
|
||||
|
||||
:::info
|
||||
**Per-Part Resolution:** Each image in your request can have its own `detail` setting, allowing mixed-resolution requests (e.g., a high-res chart alongside a low-res icon). This feature is only available for Gemini 3+ models.
|
||||
:::
|
||||
|
||||
## Sample Usage
|
||||
```python
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ response = completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -342,7 +342,7 @@ response = client.chat.completions.create(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ messages=[
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png",
|
||||
}
|
||||
},
|
||||
],
|
||||
|
|
@ -250,7 +250,7 @@ messages=[
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg",
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png",
|
||||
}
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -58,12 +58,11 @@ This method is an alternative when using the LiteLLM SDK on Oracle Cloud Infrast
|
|||
## Usage
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="manual" label="Manual Credentials">
|
||||
<TabItem value="manual" label="Manual Credentials" default>
|
||||
|
||||
Input the parameters obtained from the OCI signing key creation process into the `completion` function:
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"role": "user", "content": "Hey! how's it going?"}]
|
||||
|
|
@ -86,7 +85,7 @@ print(response)
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="oci-sdk" label="OCI SDK Signer" default>
|
||||
<TabItem value="oci-sdk" label="OCI SDK Signer">
|
||||
|
||||
Use the OCI SDK `Signer` for authentication:
|
||||
|
||||
|
|
@ -153,7 +152,6 @@ For applications running on OCI compute instances:
|
|||
from litellm import completion
|
||||
from oci.auth.signers import InstancePrincipalsSecurityTokenSigner
|
||||
|
||||
oci.auth.signers.get_oke_workload_identity_resource_principal_signer()
|
||||
# Use instance principal authentication
|
||||
signer = InstancePrincipalsSecurityTokenSigner()
|
||||
|
||||
|
|
@ -168,7 +166,7 @@ response = completion(
|
|||
print(response)
|
||||
```
|
||||
|
||||
**Use workload identity authentication**
|
||||
**Workload Identity Authentication**
|
||||
|
||||
For applications running in Oracle Kubernetes Engine (OKE):
|
||||
|
||||
|
|
@ -176,7 +174,7 @@ For applications running in Oracle Kubernetes Engine (OKE):
|
|||
from litellm import completion
|
||||
from oci.auth.signers import get_oke_workload_identity_resource_principal_signer
|
||||
|
||||
# Use instance principal authentication
|
||||
# Use workload identity authentication
|
||||
signer = get_oke_workload_identity_resource_principal_signer()
|
||||
|
||||
messages = [{"role": "user", "content": "Hey! how's it going?"}]
|
||||
|
|
@ -196,10 +194,9 @@ print(response)
|
|||
Just set `stream=True` when calling completion.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="manual-stream" label="Manual Credentials">
|
||||
<TabItem value="manual-stream" label="Manual Credentials" default>
|
||||
|
||||
```python
|
||||
import os
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"role": "user", "content": "Hey! how's it going?"}]
|
||||
|
|
@ -224,7 +221,7 @@ for chunk in response:
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="oci-sdk-stream" label="OCI SDK Signer" default>
|
||||
<TabItem value="oci-sdk-stream" label="OCI SDK Signer">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
|
@ -258,7 +255,27 @@ for chunk in response:
|
|||
### Using Cohere Models
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="cohere-sdk" label="OCI SDK Signer" default>
|
||||
<TabItem value="cohere-manual" label="Manual Credentials" default>
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"role": "user", "content": "Explain quantum computing"}]
|
||||
response = completion(
|
||||
model="oci/cohere.command-latest",
|
||||
messages=messages,
|
||||
oci_region="us-chicago-1",
|
||||
oci_user=<your_oci_user>,
|
||||
oci_fingerprint=<your_oci_fingerprint>,
|
||||
oci_tenancy=<your_oci_tenancy>,
|
||||
oci_key=<string_with_content_of_oci_key>,
|
||||
oci_compartment_id=<oci_compartment_id>,
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cohere-sdk" label="OCI SDK Signer">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
|
@ -283,19 +300,28 @@ print(response)
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="cohere-manual" label="Manual Credentials">
|
||||
</Tabs>
|
||||
|
||||
## Using Dedicated Endpoints
|
||||
|
||||
OCI supports dedicated endpoints for hosting models. Use the `oci_serving_mode="DEDICATED"` parameter along with `oci_endpoint_id` to specify the endpoint ID.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="dedicated-manual" label="Manual Credentials" default>
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
messages = [{"role": "user", "content": "Explain quantum computing"}]
|
||||
messages = [{"role": "user", "content": "Hey! how's it going?"}]
|
||||
response = completion(
|
||||
model="oci/cohere.command-latest",
|
||||
model="oci/xai.grok-4", # Must match the model type hosted on the endpoint
|
||||
messages=messages,
|
||||
oci_region="us-chicago-1",
|
||||
oci_region=<your_oci_region>,
|
||||
oci_user=<your_oci_user>,
|
||||
oci_fingerprint=<your_oci_fingerprint>,
|
||||
oci_tenancy=<your_oci_tenancy>,
|
||||
oci_serving_mode="DEDICATED",
|
||||
oci_endpoint_id="ocid1.generativeaiendpoint.oc1...", # Your dedicated endpoint OCID
|
||||
oci_key=<string_with_content_of_oci_key>,
|
||||
oci_compartment_id=<oci_compartment_id>,
|
||||
)
|
||||
|
|
@ -303,4 +329,69 @@ print(response)
|
|||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
<TabItem value="dedicated-sdk" label="OCI SDK Signer">
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
from oci.signer import Signer
|
||||
|
||||
signer = Signer(
|
||||
tenancy="ocid1.tenancy.oc1..",
|
||||
user="ocid1.user.oc1..",
|
||||
fingerprint="xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx",
|
||||
private_key_file_location="~/.oci/key.pem",
|
||||
)
|
||||
|
||||
messages = [{"role": "user", "content": "Hey! how's it going?"}]
|
||||
response = completion(
|
||||
model="oci/xai.grok-4", # Must match the model type hosted on the endpoint
|
||||
messages=messages,
|
||||
oci_signer=signer,
|
||||
oci_region="us-chicago-1",
|
||||
oci_serving_mode="DEDICATED",
|
||||
oci_endpoint_id="ocid1.generativeaiendpoint.oc1...", # Your dedicated endpoint OCID
|
||||
oci_compartment_id="<oci_compartment_id>",
|
||||
)
|
||||
print(response)
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Important:** When using `oci_serving_mode="DEDICATED"`:
|
||||
- The `model` parameter **must match the type of model hosted on your dedicated endpoint** (e.g., use `"oci/cohere.command-latest"` for Cohere models, `"oci/xai.grok-4"` for Grok models)
|
||||
- The model name determines the API format and vendor-specific handling (Cohere vs Generic)
|
||||
- The `oci_endpoint_id` parameter specifies your dedicated endpoint's OCID
|
||||
- If `oci_endpoint_id` is not provided, the `model` parameter will be used as the endpoint ID (for backward compatibility)
|
||||
|
||||
**Example with Cohere Dedicated Endpoint:**
|
||||
```python
|
||||
# For a dedicated endpoint hosting a Cohere model
|
||||
response = completion(
|
||||
model="oci/cohere.command-latest", # Use Cohere model name to get Cohere API format
|
||||
messages=messages,
|
||||
oci_region="us-chicago-1",
|
||||
oci_user=<your_oci_user>,
|
||||
oci_fingerprint=<your_oci_fingerprint>,
|
||||
oci_tenancy=<your_oci_tenancy>,
|
||||
oci_serving_mode="DEDICATED",
|
||||
oci_endpoint_id="ocid1.generativeaiendpoint.oc1...", # Your Cohere endpoint OCID
|
||||
oci_key=<string_with_content_of_oci_key>,
|
||||
oci_compartment_id=<oci_compartment_id>,
|
||||
)
|
||||
```
|
||||
|
||||
## Optional Parameters
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `oci_region` | string | `us-ashburn-1` | OCI region where the GenAI service is deployed |
|
||||
| `oci_serving_mode` | string | `ON_DEMAND` | Service mode: `ON_DEMAND` for managed models or `DEDICATED` for dedicated endpoints |
|
||||
| `oci_endpoint_id` | string | Same as `model` | (For DEDICATED mode) The OCID of your dedicated endpoint |
|
||||
| `oci_compartment_id` | string | **Required** | The OCID of the OCI compartment containing your resources |
|
||||
| `oci_user` | string | - | (Manual auth) The OCID of the OCI user |
|
||||
| `oci_fingerprint` | string | - | (Manual auth) The fingerprint of the API signing key |
|
||||
| `oci_tenancy` | string | - | (Manual auth) The OCID of your OCI tenancy |
|
||||
| `oci_key` | string | - | (Manual auth) The private key content as a string |
|
||||
| `oci_key_file` | string | - | (Manual auth) Path to the private key file |
|
||||
| `oci_signer` | object | - | (SDK auth) OCI SDK Signer object for authentication |
|
||||
|
|
@ -29,6 +29,18 @@ response = completion(
|
|||
)
|
||||
```
|
||||
|
||||
:::info Metadata passthrough (preview)
|
||||
When `litellm.enable_preview_features = True`, LiteLLM forwards only the values inside `metadata` to OpenAI.
|
||||
|
||||
```python
|
||||
completion(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "hi"}],
|
||||
metadata= {"custom_meta_key": "value"},
|
||||
)
|
||||
```
|
||||
:::
|
||||
|
||||
### Usage - LiteLLM Proxy Server
|
||||
|
||||
Here's how to call OpenAI models with the LiteLLM Proxy Server
|
||||
|
|
@ -176,6 +188,9 @@ os.environ["OPENAI_BASE_URL"] = "https://your_host/v1" # OPTIONAL
|
|||
| gpt-5-mini-2025-08-07 | `response = completion(model="gpt-5-mini-2025-08-07", messages=messages)` |
|
||||
| gpt-5-nano-2025-08-07 | `response = completion(model="gpt-5-nano-2025-08-07", messages=messages)` |
|
||||
| gpt-5-pro | `response = completion(model="gpt-5-pro", messages=messages)` |
|
||||
| gpt-5.1 | `response = completion(model="gpt-5.1", messages=messages)` |
|
||||
| gpt-5.1-codex | `response = completion(model="gpt-5.1-codex", messages=messages)` |
|
||||
| gpt-5.1-codex-mini | `response = completion(model="gpt-5.1-codex-mini", messages=messages)` |
|
||||
| gpt-4.1 | `response = completion(model="gpt-4.1", messages=messages)` |
|
||||
| gpt-4.1-mini | `response = completion(model="gpt-4.1-mini", messages=messages)` |
|
||||
| gpt-4.1-nano | `response = completion(model="gpt-4.1-nano", messages=messages)` |
|
||||
|
|
@ -237,7 +252,7 @@ response = completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -477,6 +492,8 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \
|
|||
| `gpt-5-mini` | `medium` | `none`, `minimal`, `low`, `medium`, `high` |
|
||||
| `gpt-5-nano` | `none` | `none`, `low`, `medium`, `high` |
|
||||
| `gpt-5-codex` | `adaptive` | `low`, `medium`, `high` (no `minimal`) |
|
||||
| `gpt-5.1-codex` | `adaptive` | `low`, `medium`, `high` (no `minimal`) |
|
||||
| `gpt-5.1-codex-mini` | `adaptive` | `low`, `medium`, `high` (no `minimal`) |
|
||||
| `gpt-5-pro` | `high` | `high` only |
|
||||
|
||||
**Note:**
|
||||
|
|
@ -490,7 +507,9 @@ See [OpenAI Reasoning documentation](https://platform.openai.com/docs/guides/rea
|
|||
|
||||
The `verbosity` parameter controls the length and detail of responses from GPT-5 family models. It accepts three values: `"low"`, `"medium"`, or `"high"`.
|
||||
|
||||
**Supported models:** All GPT-5 family models (`gpt-5`, `gpt-5.1`, `gpt-5-mini`, `gpt-5-nano`, `gpt-5-codex`, `gpt-5-pro`)
|
||||
**Supported models:** `gpt-5`, `gpt-5.1`, `gpt-5-mini`, `gpt-5-nano`, `gpt-5-pro`
|
||||
|
||||
**Note:** GPT-5-Codex models (`gpt-5-codex`, `gpt-5.1-codex`, `gpt-5.1-codex-mini`) do **not** support the `verbosity` parameter.
|
||||
|
||||
**Use cases:**
|
||||
- **`"low"`**: Best for concise answers or simple code generation (e.g., SQL queries)
|
||||
|
|
|
|||
|
|
@ -3,20 +3,15 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
|
||||
# Snowflake
|
||||
| Property | Details |
|
||||
|-------|-------|
|
||||
| Description | The Snowflake Cortex LLM REST API lets you access the COMPLETE function via HTTP POST requests|
|
||||
| Provider Route on LiteLLM | `snowflake/` |
|
||||
| Link to Provider Doc | [Snowflake ↗](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api) |
|
||||
| Base URL | `https://{account-id}.snowflakecomputing.com/api/v2/cortex/inference:complete` |
|
||||
| Supported OpenAI Endpoints | `/chat/completions`, `/completions` |
|
||||
| Property | Details |
|
||||
|----------------------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| Description | The Snowflake Cortex LLM REST API lets you access the COMPLETE and EMBED functions via HTTP POST requests |
|
||||
| Provider Route on LiteLLM | `snowflake/` |
|
||||
| Link to Provider Doc | [Snowflake ↗](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api) |
|
||||
| Base URLs | `https://{account-id}.snowflakecomputing.com/api/v2/cortex/inference:complete`,`https://{account-id}.snowflakecomputing.com/api/v2/cortex/inference:embed`|
|
||||
| Supported OpenAI Endpoints | `/chat/completions`, `/completions`, `/embeddings` |
|
||||
|
||||
|
||||
|
||||
Currently, Snowflake's REST API does not have an endpoint for `snowflake-arctic-embed` embedding models. If you want to use these embedding models with Litellm, you can call them through our Hugging Face provider.
|
||||
|
||||
Find the Arctic Embed models [here](https://huggingface.co/collections/Snowflake/arctic-embed-661fd57d50fab5fc314e4c18) on Hugging Face.
|
||||
|
||||
## Supported OpenAI Parameters
|
||||
```
|
||||
"temperature",
|
||||
|
|
@ -29,6 +24,9 @@ Find the Arctic Embed models [here](https://huggingface.co/collections/Snowflake
|
|||
|
||||
Snowflake does have API keys. Instead, you access the Snowflake API with your JWT token and account identifier.
|
||||
|
||||
It is also possible to use [programmatic access tokens](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens) (PAT). It can be defined by using 'pat/' prefix
|
||||
|
||||
|
||||
```python
|
||||
import os
|
||||
os.environ["SNOWFLAKE_JWT"] = "YOUR JWT"
|
||||
|
|
@ -37,17 +35,38 @@ os.environ["SNOWFLAKE_ACCOUNT_ID"] = "YOUR ACCOUNT IDENTIFIER"
|
|||
## Usage
|
||||
|
||||
```python
|
||||
from litellm import completion
|
||||
from litellm import completion, embedding
|
||||
|
||||
## set ENV variables
|
||||
os.environ["SNOWFLAKE_JWT"] = "YOUR JWT"
|
||||
os.environ["SNOWFLAKE_JWT"] = "JWT_TOKEN"
|
||||
os.environ["SNOWFLAKE_ACCOUNT_ID"] = "YOUR ACCOUNT IDENTIFIER"
|
||||
|
||||
# Snowflake call
|
||||
# Snowflake completion call
|
||||
response = completion(
|
||||
model="snowflake/mistral-7b",
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}]
|
||||
)
|
||||
|
||||
# Snowflake embedding call
|
||||
response = embedding(
|
||||
model="snowflake/mistral-7b",
|
||||
input = ["My text"]
|
||||
)
|
||||
|
||||
# Pass`api_key` and `account_id` as parameters
|
||||
response = completion(
|
||||
model="snowflake/mistral-7b",
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}],
|
||||
account_id="AAAA-BBBB",
|
||||
api_key="JWT_TOKEN"
|
||||
)
|
||||
|
||||
# using PAT
|
||||
response = completion(
|
||||
model="snowflake/mistral-7b",
|
||||
messages = [{ "content": "Hello, how are you?","role": "user"}],
|
||||
api_key="pat/PAT_TOKEN"
|
||||
)
|
||||
```
|
||||
|
||||
## Usage with LiteLLM Proxy
|
||||
|
|
|
|||
|
|
@ -1741,7 +1741,7 @@ response = litellm.completion(
|
|||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
|
||||
"url": "https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,68 @@ https://docs.x.ai/docs
|
|||
|
||||
:::
|
||||
|
||||
## Supported Models
|
||||
|
||||
|
||||
|
||||
**Latest Release** - Grok 4.1 Fast: Optimized for high-performance agentic tool calling with 2M context and prompt caching.
|
||||
|
||||
| Model | Context | Features |
|
||||
|-------|---------|----------|
|
||||
| `xai/grok-4-1-fast-reasoning` | 2M tokens | **Reasoning**, Function calling, Vision, Audio, Web search, Caching |
|
||||
| `xai/grok-4-1-fast-non-reasoning` | 2M tokens | Function calling, Vision, Audio, Web search, Caching |
|
||||
|
||||
**When to use:**
|
||||
- ✅ **Reasoning model**: Complex analysis, planning, multi-step reasoning problems
|
||||
- ✅ **Non-reasoning model**: Simple queries, faster responses, lower token usage
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
from litellm import completion
|
||||
|
||||
# With reasoning
|
||||
response = completion(
|
||||
model="xai/grok-4-1-fast-reasoning",
|
||||
messages=[{"role": "user", "content": "Analyze this problem step by step..."}]
|
||||
)
|
||||
|
||||
# Without reasoning
|
||||
response = completion(
|
||||
model="xai/grok-4-1-fast-non-reasoning",
|
||||
messages=[{"role": "user", "content": "What's 2+2?"}]
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### All Available Models
|
||||
|
||||
| Model Family | Model | Context | Features |
|
||||
|--------------|-------|---------|----------|
|
||||
| **Grok 4.1** | `xai/grok-4-1-fast-reasoning` | 2M | **Reasoning**, Tools, Vision, Audio, Web search, Caching |
|
||||
| | `xai/grok-4-1-fast-non-reasoning` | 2M | Tools, Vision, Audio, Web search, Caching |
|
||||
| **Grok 4** | `xai/grok-4` | 256K | Tools, Web search |
|
||||
| | `xai/grok-4-0709` | 256K | Tools, Web search |
|
||||
| | `xai/grok-4-fast-reasoning` | 2M | **Reasoning**, Tools, Web search |
|
||||
| | `xai/grok-4-fast-non-reasoning` | 2M | Tools, Web search |
|
||||
| **Grok 3** | `xai/grok-3` | 131K | Tools, Web search |
|
||||
| | `xai/grok-3-mini` | 131K | Tools, Web search |
|
||||
| | `xai/grok-3-fast-beta` | 131K | Tools, Web search |
|
||||
| **Grok Code** | `xai/grok-code-fast` | 256K | **Reasoning**, Tools, Code generation, Caching |
|
||||
| **Grok 2** | `xai/grok-2` | 131K | Tools, **Vision** |
|
||||
| | `xai/grok-2-vision-latest` | 32K | Tools, **Vision** |
|
||||
|
||||
**Features:**
|
||||
- **Reasoning** = Chain-of-thought reasoning with reasoning tokens
|
||||
- **Tools** = Function calling / Tool use
|
||||
- **Web search** = Live internet search
|
||||
- **Vision** = Image understanding
|
||||
- **Audio** = Audio input support
|
||||
- **Caching** = Prompt caching for cost savings
|
||||
- **Code generation** = Optimized for code tasks
|
||||
|
||||
**Pricing:** See [xAI's pricing page](https://docs.x.ai/docs/models) for current rates.
|
||||
|
||||
## API Key
|
||||
```python
|
||||
# env variable
|
||||
|
|
|
|||
|
|
@ -380,3 +380,54 @@ If you need to inspect the JWT fields received from your SSO provider by LiteLLM
|
|||
|
||||
Once redirected, you should see a page called "SSO Debug Information". This page displays the JWT fields received from your SSO provider (as shown in the image above)
|
||||
|
||||
|
||||
## Advanced
|
||||
|
||||
### Manage User Roles via Azure App Roles
|
||||
|
||||
Centralize role management by defining user permissions in Azure Entra ID. LiteLLM will automatically assign roles based on your Azure configuration when users sign in—no need to manually manage roles in LiteLLM.
|
||||
|
||||
#### Step 1: Create App Roles on Azure App Registration
|
||||
|
||||
1. Navigate to your App Registration on https://portal.azure.com/
|
||||
2. Go to **App roles** > **Create app role**
|
||||
3. Configure the app role using one of the [supported LiteLLM roles](./access_control.md#global-proxy-roles):
|
||||
- **Display name**: Admin Viewer (or your preferred display name)
|
||||
- **Value**: `proxy_admin_viewer` (must match one of the LiteLLM role values exactly)
|
||||
4. Click **Apply** to save the role
|
||||
5. Repeat for each LiteLLM role you want to use
|
||||
|
||||
|
||||
**Supported LiteLLM role values** (see [full role documentation](./access_control.md#global-proxy-roles)):
|
||||
- `proxy_admin` - Full admin access
|
||||
- `proxy_admin_viewer` - Read-only admin access
|
||||
- `internal_user` - Can create/view/delete own keys
|
||||
- `internal_user_viewer` - Can view own keys (read-only)
|
||||
|
||||
<Image img={require('../../img/app_roles.png')} style={{ width: '900px', height: 'auto' }} />
|
||||
|
||||
---
|
||||
|
||||
#### Step 2: Assign Users to App Roles
|
||||
|
||||
1. Navigate to **Enterprise Applications** on https://portal.azure.com/
|
||||
2. Select your LiteLLM application
|
||||
3. Go to **Users and groups** > **Add user/group**
|
||||
4. Select the user
|
||||
5. Under **Select a role**, choose the app role you created (e.g., `proxy_admin_viewer`)
|
||||
6. Click **Assign** to save
|
||||
|
||||
<Image img={require('../../img/app_role2.png')} style={{ width: '900px', height: 'auto' }} />
|
||||
|
||||
---
|
||||
|
||||
#### Step 3: Sign in and verify
|
||||
|
||||
1. Sign in to the LiteLLM UI via SSO
|
||||
2. LiteLLM will automatically extract the app role from the JWT token
|
||||
3. The user will be assigned the corresponding role (you can verify this in the UI by checking the user profile dropdown)
|
||||
|
||||
<Image img={require('../../img/app_role3.png')} style={{ width: '900px', height: 'auto' }} />
|
||||
|
||||
**Note:** The role from Entra ID will take precedence over any existing role in the LiteLLM database. This ensures your SSO provider is the authoritative source for user roles.
|
||||
|
||||
|
|
|
|||
240
docs/my-website/docs/proxy/ai_hub.md
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# AI Hub
|
||||
|
||||
Share models and agents with your organization. Show developers what's available without needing to rebuild them.
|
||||
|
||||
This feature is **available in v1.74.3-stable and above**.
|
||||
|
||||
## Overview
|
||||
|
||||
Admin can select models/agents to expose on public AI hub → Users go to the public url and see what's available.
|
||||
|
||||
<Image img={require('../../img/final_public_model_hub_view.png')} />
|
||||
|
||||
## Models
|
||||
|
||||
### How to use
|
||||
|
||||
#### 1. Go to the Admin UI
|
||||
|
||||
Navigate to the Model Hub page in the Admin UI (`PROXY_BASE_URL/ui/?login=success&page=model-hub-table`)
|
||||
|
||||
<Image img={require('../../img/model_hub_admin_view.png')} />
|
||||
|
||||
#### 2. Select the models you want to expose
|
||||
|
||||
Click on `Select Models to Make Public` and select the models you want to expose.
|
||||
|
||||
<Image img={require('../../img/make_public_modal.png')} />
|
||||
|
||||
#### 3. Confirm the changes
|
||||
|
||||
<Image img={require('../../img/make_public_modal_confirmation.png')} />
|
||||
|
||||
#### 4. Success!
|
||||
|
||||
Go to the public url (`PROXY_BASE_URL/ui/model_hub_table`) and see available models.
|
||||
|
||||
<Image img={require('../../img/final_public_model_hub_view.png')} />
|
||||
|
||||
### API Endpoints
|
||||
|
||||
- `GET /public/model_hub` – returns the list of public model groups. Requires a valid user API key.
|
||||
- `GET /public/model_hub/info` – returns metadata (docs title, version, useful links) for the public model hub.
|
||||
|
||||
## Agents
|
||||
|
||||
:::info
|
||||
Agents are only available in v1.79.4-stable and above.
|
||||
:::
|
||||
|
||||
Share pre-built agents (A2A spec) across your organization. Users can discover and use agents without rebuilding them.
|
||||
|
||||
[**Demo Video**](https://drive.google.com/file/d/1r-_Rtiu04RW5Fwwu3_eshtA1oZtC3_DH/view?usp=sharing)
|
||||
|
||||
### 1. Create an agent
|
||||
|
||||
Create an agent that follows the [A2A spec](https://a2a.dev/).
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="ui" label="UI">
|
||||
|
||||
<Image img={require('../../img/add_agent.png')} />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="api" label="API">
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/agents' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"agent_name": "hello-world-agent",
|
||||
"agent_card_params": {
|
||||
"protocolVersion": "1.0",
|
||||
"name": "Hello World Agent",
|
||||
"description": "Just a hello world agent",
|
||||
"url": "http://localhost:9999/",
|
||||
"version": "1.0.0",
|
||||
"defaultInputModes": ["text"],
|
||||
"defaultOutputModes": ["text"],
|
||||
"capabilities": {
|
||||
"streaming": true
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"id": "hello_world",
|
||||
"name": "Returns hello world",
|
||||
"description": "just returns hello world",
|
||||
"tags": ["hello world"],
|
||||
"examples": ["hi", "hello world"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"agent_name": "hello-world-agent",
|
||||
"agent_card_params": {
|
||||
"protocolVersion": "1.0",
|
||||
"name": "Hello World Agent",
|
||||
"description": "Just a hello world agent",
|
||||
"url": "http://localhost:9999/",
|
||||
"version": "1.0.0",
|
||||
"defaultInputModes": ["text"],
|
||||
"defaultOutputModes": ["text"],
|
||||
"capabilities": {
|
||||
"streaming": true
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"id": "hello_world",
|
||||
"name": "Returns hello world",
|
||||
"description": "just returns hello world",
|
||||
"tags": ["hello world"],
|
||||
"examples": ["hi", "hello world"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"created_at": "2025-11-15T10:30:00Z",
|
||||
"created_by": "user123"
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### 2. Make agent public
|
||||
|
||||
Make the agent discoverable on the AI Hub.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="ui" label="UI">
|
||||
|
||||
Navigate to the Agents Tab on the AI Hub page
|
||||
|
||||
<Image img={require('../../img/ai_hub_with_agents.png')} />
|
||||
|
||||
Select the agents you want to make public and click on `Make Public` button.
|
||||
|
||||
<Image img={require('../../img/make_agents_public.png')} />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="api" label="API">
|
||||
|
||||
**Option 1: Make single agent public**
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/agents/123e4567-e89b-12d3-a456-426614174000/make_public' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
**Option 2: Make multiple agents public**
|
||||
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://0.0.0.0:4000/v1/agents/make_public' \
|
||||
--header 'Authorization: Bearer <your-master-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"agent_ids": [
|
||||
"123e4567-e89b-12d3-a456-426614174000",
|
||||
"123e4567-e89b-12d3-a456-426614174001"
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Successfully updated public agent groups",
|
||||
"public_agent_groups": [
|
||||
"123e4567-e89b-12d3-a456-426614174000"
|
||||
],
|
||||
"updated_by": "user123"
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
|
||||
### 3. View public agents
|
||||
|
||||
Users can now discover the agent via the public endpoint.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="ui" label="UI">
|
||||
|
||||
<Image img={require('../../img/public_agent_hub.png')} />
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="api" label="API">
|
||||
|
||||
```bash
|
||||
curl -X GET 'http://0.0.0.0:4000/public/agent_hub' \
|
||||
--header 'Authorization: Bearer <user-api-key>'
|
||||
```
|
||||
|
||||
**Expected Response**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"protocolVersion": "1.0",
|
||||
"name": "Hello World Agent",
|
||||
"description": "Just a hello world agent",
|
||||
"url": "http://localhost:9999/",
|
||||
"version": "1.0.0",
|
||||
"defaultInputModes": ["text"],
|
||||
"defaultOutputModes": ["text"],
|
||||
"capabilities": {
|
||||
"streaming": true
|
||||
},
|
||||
"skills": [
|
||||
{
|
||||
"id": "hello_world",
|
||||
"name": "Returns hello world",
|
||||
"description": "just returns hello world",
|
||||
"tags": ["hello world"],
|
||||
"examples": ["hi", "hello world"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
|
@ -9,6 +9,26 @@ Use the litellm cli to authenticate to the LiteLLM Gateway. This is great if you
|
|||
|
||||
## Usage
|
||||
|
||||
### Prerequisites - Start LiteLLM Proxy with Beta Flag
|
||||
|
||||
:::warning[Beta Feature - Required]
|
||||
|
||||
CLI SSO Authentication is currently in beta. You must set this environment variable **when starting up your LiteLLM Proxy**:
|
||||
|
||||
```bash
|
||||
export EXPERIMENTAL_UI_LOGIN="True"
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
Or add it to your proxy startup command:
|
||||
|
||||
```bash
|
||||
EXPERIMENTAL_UI_LOGIN="True" litellm --config config.yaml
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Install the CLI**
|
||||
|
||||
|
|
@ -33,6 +53,8 @@ Use the litellm cli to authenticate to the LiteLLM Gateway. This is great if you
|
|||
|
||||
2. **Set up environment variables**
|
||||
|
||||
On your local machine, set the proxy URL:
|
||||
|
||||
```bash
|
||||
export LITELLM_PROXY_URL=http://localhost:4000
|
||||
```
|
||||
|
|
|
|||
|
|
@ -655,6 +655,7 @@ router_settings:
|
|||
| LITELLM_GLOBAL_MAX_PARALLEL_REQUEST_RETRY_TIMEOUT | Timeout for retries of parallel requests in LiteLLM
|
||||
| LITELLM_MIGRATION_DIR | Custom migrations directory for prisma migrations, used for baselining db in read-only file systems.
|
||||
| LITELLM_HOSTED_UI | URL of the hosted UI for LiteLLM
|
||||
| LITELLM_UI_API_DOC_BASE_URL | Optional override for the API Reference base URL (used in sample code/docs) when the admin UI runs on a different host than the proxy. Defaults to `PROXY_BASE_URL` when unset.
|
||||
| LITELM_ENVIRONMENT | Environment of LiteLLM Instance, used by logging services. Currently only used by DeepEval.
|
||||
| LITELLM_KEY_ROTATION_ENABLED | Enable auto-key rotation for LiteLLM (boolean). Default is false.
|
||||
| LITELLM_KEY_ROTATION_CHECK_INTERVAL_SECONDS | Interval in seconds for how often to run job that auto-rotates keys. Default is 86400 (24 hours).
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ DISABLE_LLM_API_ENDPOINTS=true
|
|||
- `/config/*` - Configuration updates
|
||||
- All other administrative endpoints
|
||||
|
||||
### `LITELLM_UI_API_DOC_BASE_URL`
|
||||
|
||||
Optional override for the API Reference base URL (used in sample code/docs) when the admin UI runs on a different host than the proxy.
|
||||
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ You can see the full DB Schema [here](https://github.com/BerriAI/litellm/blob/ma
|
|||
|
||||
| Table Name | Description | Row Insert Frequency |
|
||||
|------------|-------------|---------------------|
|
||||
| LiteLLM_SpendLogs | Detailed logs of all API requests. Records token usage, spend, and timing information. Tracks which models and keys were used. | **High - every LLM API request - Success or Failure** |
|
||||
| LiteLLM_AuditLog | Tracks changes to system configuration. Records who made changes and what was modified. Maintains history of updates to teams, users, and models. | **Off by default**, **High - when enabled** |
|
||||
| LiteLLM_SpendLogs | Detailed logs of all API requests. Records token usage, spend, and timing information. Tracks which models and keys were used. | **Medium - this is a batch process that runs on an interval.** |
|
||||
| LiteLLM_AuditLog | Tracks changes to system configuration. Records who made changes and what was modified. Maintains history of updates to teams, users, and models. | **Off by default**, **High - Runs on every change to an entity** |
|
||||
|
||||
## Disable `LiteLLM_SpendLogs`
|
||||
|
||||
|
|
|
|||
|
|
@ -211,4 +211,64 @@ x-litellm-disable-callbacks: LANGFUSE,datadog,PROMETHEUS
|
|||
x-litellm-disable-callbacks: langfuse,DATADOG,prometheus
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Disabling Dynamic Callback Management (Enterprise)
|
||||
|
||||
Some organizations have compliance requirements where **all requests must be logged under all circumstances**. For these cases, you can disable dynamic callback management entirely to ensure users cannot disable any logging callbacks.
|
||||
|
||||
### Use Case
|
||||
|
||||
This is designed for enterprise scenarios where:
|
||||
- **Compliance requirements** mandate that all API requests must be logged
|
||||
- **Audit trails** must be complete with no gaps
|
||||
- **Security policies** require all traffic to be monitored
|
||||
- **No exceptions** can be made for callback disabling
|
||||
|
||||
### How to Disable
|
||||
|
||||
Set `allow_dynamic_callback_disabling` to `false` in your config.yaml:
|
||||
|
||||
```yaml showLineNumbers title="config.yaml"
|
||||
litellm_settings:
|
||||
allow_dynamic_callback_disabling: false
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
When disabled:
|
||||
- The `x-litellm-disable-callbacks` header will be **ignored**
|
||||
- All configured callbacks will **always execute** for every request
|
||||
- Users cannot bypass logging through headers or request metadata
|
||||
- All requests are guaranteed to be logged per your proxy configuration
|
||||
|
||||
### Example: Compliance Logging Setup
|
||||
|
||||
Here's a complete example for an organization requiring guaranteed logging:
|
||||
|
||||
```yaml showLineNumbers title="config.yaml"
|
||||
# config.yaml
|
||||
model_list:
|
||||
- model_name: gpt-4
|
||||
litellm_params:
|
||||
model: openai/gpt-4
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
litellm_settings:
|
||||
callbacks: ["langfuse", "datadog", "s3"]
|
||||
# Disable dynamic callback disabling for compliance
|
||||
allow_dynamic_callback_disabling: false
|
||||
```
|
||||
|
||||
With this configuration:
|
||||
- All requests will be logged to Langfuse, Datadog, and S3
|
||||
- Users cannot disable any of these callbacks via headers
|
||||
- Complete audit trail is guaranteed for compliance requirements
|
||||
|
||||
:::info
|
||||
|
||||
**Default Behavior**: Dynamic callback disabling is **enabled by default** (`allow_dynamic_callback_disabling: true`). You must explicitly set it to `false` to enforce guaranteed logging.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -901,9 +901,11 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
'
|
||||
```
|
||||
|
||||
## Public Model Hub
|
||||
## Public AI Hub
|
||||
|
||||
Share a public page of available models for users
|
||||
Share a public page of available models and agents for users
|
||||
|
||||
[Learn more](./ai_hub.md)
|
||||
|
||||
<Image img={require('../../img/model_hub.png')} style={{ width: '900px', height: 'auto' }}/>
|
||||
|
||||
|
|
|
|||
|
|
@ -4,151 +4,86 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
# Custom Guardrail
|
||||
|
||||
Use this is you want to write code to run a custom guardrail
|
||||
Use this if you want to write code to run a custom guardrail
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Write a `CustomGuardrail` Class
|
||||
|
||||
A CustomGuardrail has 4 methods to enforce guardrails
|
||||
- `async_pre_call_hook` - (Optional) modify input or reject request before making LLM API call
|
||||
- `async_moderation_hook` - (Optional) reject request, runs while making LLM API call (help to lower latency)
|
||||
- `async_post_call_success_hook`- (Optional) apply guardrail on input/output, runs after making LLM API call
|
||||
- `async_post_call_streaming_iterator_hook` - (Optional) pass the entire stream to the guardrail
|
||||
|
||||
|
||||
**[See detailed spec of methods here](#customguardrail-methods)**
|
||||
The simplest way to create a custom guardrail is by implementing the `apply_guardrail` method. This method is called to check text content and can block requests by raising an exception.
|
||||
|
||||
**Example `CustomGuardrail` Class**
|
||||
|
||||
Create a new file called `custom_guardrail.py` and add this code to it
|
||||
Create a new file called `custom_guardrail.py` and add this code to it:
|
||||
|
||||
```python
|
||||
from typing import Any, AsyncGenerator, Literal, Optional, Union
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.caching.caching import DualCache
|
||||
import os
|
||||
from typing import Optional, List
|
||||
from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.types.utils import ModelResponseStream
|
||||
|
||||
from litellm.types.guardrails import PiiEntityType
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.llms.custom_httpx.http_handler import (
|
||||
get_async_httpx_client,
|
||||
httpxSpecialProvider,
|
||||
)
|
||||
|
||||
class myCustomGuardrail(CustomGuardrail):
|
||||
def __init__(
|
||||
self,
|
||||
**kwargs,
|
||||
):
|
||||
# store kwargs as optional_params
|
||||
self.optional_params = kwargs
|
||||
|
||||
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs):
|
||||
self.api_key = api_key or os.getenv("MY_GUARDRAIL_API_KEY")
|
||||
self.api_base = api_base or os.getenv("MY_GUARDRAIL_API_BASE", "https://api.myguardrail.com")
|
||||
super().__init__(**kwargs)
|
||||
|
||||
async def async_pre_call_hook(
|
||||
async def apply_guardrail(
|
||||
self,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
cache: DualCache,
|
||||
data: dict,
|
||||
call_type: Literal[
|
||||
"completion",
|
||||
"text_completion",
|
||||
"embeddings",
|
||||
"image_generation",
|
||||
"moderation",
|
||||
"audio_transcription",
|
||||
"pass_through_endpoint",
|
||||
"rerank"
|
||||
],
|
||||
) -> Optional[Union[Exception, str, dict]]:
|
||||
text: str, # IMPORTANT: This is the text to check against your guardrail rules. It's extracted from the request or response across all LLM call types.
|
||||
language: Optional[str] = None, # ignore
|
||||
entities: Optional[List[PiiEntityType]] = None, # ignore
|
||||
request_data: Optional[dict] = None, # ignore
|
||||
) -> str:
|
||||
"""
|
||||
Runs before the LLM API call
|
||||
Runs on only Input
|
||||
Use this if you want to MODIFY the input
|
||||
Check text content against your guardrail rules.
|
||||
Raise an exception to block the request.
|
||||
Return the text (optionally modified) to allow it through.
|
||||
"""
|
||||
result = await self._check_with_api(text, request_data)
|
||||
|
||||
if result.get("action") == "BLOCK":
|
||||
raise Exception(f"Content blocked: {result.get('reason', 'Policy violation')}")
|
||||
|
||||
return text
|
||||
|
||||
# In this guardrail, if a user inputs `litellm` we will mask it and then send it to the LLM
|
||||
_messages = data.get("messages")
|
||||
if _messages:
|
||||
for message in _messages:
|
||||
_content = message.get("content")
|
||||
if isinstance(_content, str):
|
||||
if "litellm" in _content.lower():
|
||||
_content = _content.replace("litellm", "********")
|
||||
message["content"] = _content
|
||||
|
||||
verbose_proxy_logger.debug(
|
||||
"async_pre_call_hook: Message after masking %s", _messages
|
||||
async def _check_with_api(self, text: str, request_data: Optional[dict]) -> dict:
|
||||
async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {self.api_key}",
|
||||
}
|
||||
|
||||
response = await async_client.post(
|
||||
f"{self.api_base}/check",
|
||||
headers=headers,
|
||||
json={"text": text},
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
async def async_moderation_hook(
|
||||
self,
|
||||
data: dict,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
call_type: Literal["completion", "embeddings", "image_generation", "moderation", "audio_transcription"],
|
||||
):
|
||||
"""
|
||||
Runs in parallel to LLM API call
|
||||
Runs on only Input
|
||||
|
||||
This can NOT modify the input, only used to reject or accept a call before going to LLM API
|
||||
"""
|
||||
|
||||
# this works the same as async_pre_call_hook, but just runs in parallel as the LLM API Call
|
||||
# In this guardrail, if a user inputs `litellm` we will mask it.
|
||||
_messages = data.get("messages")
|
||||
if _messages:
|
||||
for message in _messages:
|
||||
_content = message.get("content")
|
||||
if isinstance(_content, str):
|
||||
if "litellm" in _content.lower():
|
||||
raise ValueError("Guardrail failed words - `litellm` detected")
|
||||
|
||||
async def async_post_call_success_hook(
|
||||
self,
|
||||
data: dict,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
response,
|
||||
):
|
||||
"""
|
||||
Runs on response from LLM API call
|
||||
|
||||
It can be used to reject a response
|
||||
|
||||
If a response contains the word "coffee" -> we will raise an exception
|
||||
"""
|
||||
verbose_proxy_logger.debug("async_pre_call_hook response: %s", response)
|
||||
if isinstance(response, litellm.ModelResponse):
|
||||
for choice in response.choices:
|
||||
if isinstance(choice, litellm.Choices):
|
||||
verbose_proxy_logger.debug("async_pre_call_hook choice: %s", choice)
|
||||
if (
|
||||
choice.message.content
|
||||
and isinstance(choice.message.content, str)
|
||||
and "coffee" in choice.message.content
|
||||
):
|
||||
raise ValueError("Guardrail failed Coffee Detected")
|
||||
|
||||
async def async_post_call_streaming_iterator_hook(
|
||||
self,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
response: Any,
|
||||
request_data: dict,
|
||||
) -> AsyncGenerator[ModelResponseStream, None]:
|
||||
"""
|
||||
Passes the entire stream to the guardrail
|
||||
|
||||
This is useful for guardrails that need to see the entire response, such as PII masking.
|
||||
|
||||
See Aim guardrail implementation for an example - https://github.com/BerriAI/litellm/blob/d0e022cfacb8e9ebc5409bb652059b6fd97b45c0/litellm/proxy/guardrails/guardrail_hooks/aim.py#L168
|
||||
|
||||
Triggered by mode: 'post_call'
|
||||
"""
|
||||
async for item in response:
|
||||
yield item
|
||||
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
```
|
||||
|
||||
:::tip Advanced: Using Individual Event Hooks
|
||||
|
||||
If you need more fine-grained control, you can implement individual event hooks instead of (or in addition to) `apply_guardrail`:
|
||||
|
||||
- `async_pre_call_hook` - Modify input or reject request before making LLM API call
|
||||
- `async_moderation_hook` - Reject request, runs in parallel with LLM API call (helps lower latency)
|
||||
- `async_post_call_success_hook` - Apply guardrail on input/output, runs after making LLM API call
|
||||
- `async_post_call_streaming_iterator_hook` - Pass the entire stream to the guardrail
|
||||
|
||||
**[See examples of individual event hooks here](#advanced-individual-event-hooks)** | **[See detailed spec of methods here](#customguardrail-methods)**
|
||||
|
||||
:::
|
||||
|
||||
### 2. Pass your custom guardrail class in LiteLLM `config.yaml`
|
||||
|
||||
In the config below, we point the guardrail to our custom guardrail by setting `guardrail: custom_guardrail.myCustomGuardrail`
|
||||
|
|
@ -166,9 +101,32 @@ model_list:
|
|||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
guardrails:
|
||||
- guardrail_name: "custom-pre-guard"
|
||||
- guardrail_name: "my-custom-guardrail"
|
||||
litellm_params:
|
||||
guardrail: custom_guardrail.myCustomGuardrail # 👈 Key change
|
||||
mode: "during_call" # runs apply_guardrail method
|
||||
api_key: os.environ/MY_GUARDRAIL_API_KEY
|
||||
api_base: https://api.myguardrail.com
|
||||
```
|
||||
|
||||
:::info Mode Options
|
||||
|
||||
- `during_call` - Default mode, runs `apply_guardrail` method (or `async_moderation_hook` if using individual hooks)
|
||||
- `pre_call` - Runs `async_pre_call_hook` for input modification
|
||||
- `post_call` - Runs `async_post_call_success_hook` for output validation
|
||||
|
||||
:::
|
||||
|
||||
<details>
|
||||
<summary>Advanced: Multiple modes with individual event hooks</summary>
|
||||
|
||||
If you're using individual event hooks, you can configure multiple guardrails with different modes:
|
||||
|
||||
```yaml
|
||||
guardrails:
|
||||
- guardrail_name: "custom-pre-guard"
|
||||
litellm_params:
|
||||
guardrail: custom_guardrail.myCustomGuardrail
|
||||
mode: "pre_call" # runs async_pre_call_hook
|
||||
- guardrail_name: "custom-during-guard"
|
||||
litellm_params:
|
||||
|
|
@ -180,6 +138,8 @@ guardrails:
|
|||
mode: "post_call" # runs async_post_call_success_hook
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### 3. Start LiteLLM Gateway
|
||||
|
||||
<Tabs>
|
||||
|
|
@ -218,15 +178,76 @@ litellm --config config.yaml --detailed_debug
|
|||
|
||||
### 4. Test it
|
||||
|
||||
#### Test `"custom-pre-guard"`
|
||||
|
||||
|
||||
**[Langchain, OpenAI SDK Usage Examples](../proxy/user_keys#request-format)**
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Blocked Request" value = "blocked">
|
||||
|
||||
This request will be blocked if it violates your guardrail policy:
|
||||
|
||||
```shell
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-4",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Content that violates policy"
|
||||
}
|
||||
],
|
||||
"guardrails": ["my-custom-guardrail"]
|
||||
}'
|
||||
```
|
||||
|
||||
Expected response when blocked:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"message": "Content blocked: Policy violation",
|
||||
"type": "None",
|
||||
"param": "None",
|
||||
"code": "500"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Successful Call" value = "allowed">
|
||||
|
||||
This request passes the guardrail:
|
||||
|
||||
```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": "What is the weather like today?"}
|
||||
],
|
||||
"guardrails": ["my-custom-guardrail"]
|
||||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
</Tabs>
|
||||
|
||||
<details>
|
||||
<summary>Advanced: Testing individual event hooks</summary>
|
||||
|
||||
If you're using individual event hooks, you can test each mode separately:
|
||||
|
||||
#### Test `"custom-pre-guard"`
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Modify input" value = "not-allowed">
|
||||
|
||||
Expect this to mask the word `litellm` before sending the request to the LLM API. [This runs the `async_pre_call_hook`](#1-write-a-customguardrail-class)
|
||||
Expect this to mask the word `litellm` before sending the request to the LLM API. [This runs the `async_pre_call_hook`](#advanced-individual-event-hooks)
|
||||
|
||||
```shell
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
|
|
@ -244,37 +265,6 @@ curl -i -X POST http://localhost:4000/v1/chat/completions \
|
|||
}'
|
||||
```
|
||||
|
||||
Expected response after pre-guard
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "chatcmpl-9zREDkBIG20RJB4pMlyutmi1hXQWc",
|
||||
"choices": [
|
||||
{
|
||||
"finish_reason": "stop",
|
||||
"index": 0,
|
||||
"message": {
|
||||
"content": "It looks like you've chosen a string of asterisks. This could be a way to censor or hide certain text. However, without more context, I can't provide a specific word or phrase. If there's something specific you'd like me to say or if you need help with a topic, feel free to let me know!",
|
||||
"role": "assistant",
|
||||
"tool_calls": null,
|
||||
"function_call": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"created": 1724429701,
|
||||
"model": "gpt-4o-2024-05-13",
|
||||
"object": "chat.completion",
|
||||
"system_fingerprint": "fp_3aa7262c27",
|
||||
"usage": {
|
||||
"completion_tokens": 65,
|
||||
"prompt_tokens": 14,
|
||||
"total_tokens": 79
|
||||
},
|
||||
"service_tier": null
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem label="Successful Call " value = "allowed">
|
||||
|
|
@ -282,7 +272,7 @@ Expected response after pre-guard
|
|||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-npnwjPQciVRok5yNZgKmFQ" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
|
|
@ -294,20 +284,14 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
|
||||
</TabItem>
|
||||
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
#### Test `"custom-during-guard"`
|
||||
|
||||
|
||||
**[Langchain, OpenAI SDK Usage Examples](../proxy/user_keys#request-format)**
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Unsuccessful call" value = "not-allowed">
|
||||
|
||||
Expect this to fail since since `litellm` is in the message content. [This runs the `async_moderation_hook`](#1-write-a-customguardrail-class)
|
||||
|
||||
Expect this to fail since `litellm` is in the message content. [This runs the `async_moderation_hook`](#advanced-individual-event-hooks)
|
||||
|
||||
```shell
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
|
|
@ -325,7 +309,7 @@ curl -i -X POST http://localhost:4000/v1/chat/completions \
|
|||
}'
|
||||
```
|
||||
|
||||
Expected response after running during-guard
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -345,7 +329,7 @@ Expected response after running during-guard
|
|||
```shell
|
||||
curl -i http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-npnwjPQciVRok5yNZgKmFQ" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"messages": [
|
||||
|
|
@ -357,21 +341,14 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
|
||||
</TabItem>
|
||||
|
||||
|
||||
</Tabs>
|
||||
|
||||
|
||||
#### Test `"custom-post-guard"`
|
||||
|
||||
|
||||
|
||||
**[Langchain, OpenAI SDK Usage Examples](../proxy/user_keys#request-format)**
|
||||
|
||||
<Tabs>
|
||||
<TabItem label="Unsuccessful call" value = "not-allowed">
|
||||
|
||||
Expect this to fail since since `coffee` will be in the response content. [This runs the `async_post_call_success_hook`](#1-write-a-customguardrail-class)
|
||||
|
||||
Expect this to fail since `coffee` will be in the response content. [This runs the `async_post_call_success_hook`](#advanced-individual-event-hooks)
|
||||
|
||||
```shell
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
|
|
@ -389,7 +366,7 @@ curl -i -X POST http://localhost:4000/v1/chat/completions \
|
|||
}'
|
||||
```
|
||||
|
||||
Expected response after running during-guard
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
@ -407,7 +384,7 @@ Expected response after running during-guard
|
|||
<TabItem label="Successful Call " value = "allowed">
|
||||
|
||||
```shell
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-d '{
|
||||
|
|
@ -424,9 +401,10 @@ Expected response after running during-guard
|
|||
|
||||
</TabItem>
|
||||
|
||||
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
## ✨ Pass additional parameters to guardrail
|
||||
|
||||
:::info
|
||||
|
|
@ -539,10 +517,162 @@ The `get_guardrail_dynamic_request_body_params` method will return:
|
|||
}
|
||||
```
|
||||
|
||||
## Advanced: Individual Event Hooks
|
||||
|
||||
Pro: More flexibility
|
||||
Con: You need to implement this for each LLM call type (chat completions, text completions, embeddings, image generation, moderation, audio transcription, pass through endpoint, rerank, etc. )
|
||||
|
||||
For more fine-grained control over when and how your guardrail runs, you can implement individual event hooks. This gives you flexibility to:
|
||||
- Modify inputs before the LLM call
|
||||
- Run checks in parallel with the LLM call (lower latency)
|
||||
- Validate or modify outputs after the LLM call
|
||||
- Process streaming responses
|
||||
|
||||
### Example with Individual Event Hooks
|
||||
|
||||
```python
|
||||
from typing import Any, AsyncGenerator, Literal, Optional, Union
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.caching.caching import DualCache
|
||||
from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.proxy._types import UserAPIKeyAuth
|
||||
from litellm.types.utils import ModelResponseStream, CallTypes
|
||||
|
||||
|
||||
class myCustomGuardrail(CustomGuardrail):
|
||||
def __init__(
|
||||
self,
|
||||
**kwargs,
|
||||
):
|
||||
# store kwargs as optional_params
|
||||
self.optional_params = kwargs
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
async def async_pre_call_hook(
|
||||
self,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
cache: DualCache,
|
||||
data: dict,
|
||||
call_type: Optional[CallTypes],
|
||||
) -> Optional[Union[Exception, str, dict]]:
|
||||
"""
|
||||
Runs before the LLM API call
|
||||
Runs on only Input
|
||||
Use this if you want to MODIFY the input
|
||||
"""
|
||||
|
||||
# In this guardrail, if a user inputs `litellm` we will mask it and then send it to the LLM
|
||||
_messages = data.get("messages")
|
||||
if _messages:
|
||||
for message in _messages:
|
||||
_content = message.get("content")
|
||||
if isinstance(_content, str):
|
||||
if "litellm" in _content.lower():
|
||||
_content = _content.replace("litellm", "********")
|
||||
message["content"] = _content
|
||||
|
||||
verbose_proxy_logger.debug(
|
||||
"async_pre_call_hook: Message after masking %s", _messages
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
async def async_moderation_hook(
|
||||
self,
|
||||
data: dict,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
call_type: Literal["completion", "embeddings", "image_generation", "moderation", "audio_transcription"],
|
||||
):
|
||||
"""
|
||||
Runs in parallel to LLM API call
|
||||
Runs on only Input
|
||||
|
||||
This can NOT modify the input, only used to reject or accept a call before going to LLM API
|
||||
"""
|
||||
|
||||
# this works the same as async_pre_call_hook, but just runs in parallel as the LLM API Call
|
||||
# In this guardrail, if a user inputs `litellm` we will mask it.
|
||||
_messages = data.get("messages")
|
||||
if _messages:
|
||||
for message in _messages:
|
||||
_content = message.get("content")
|
||||
if isinstance(_content, str):
|
||||
if "litellm" in _content.lower():
|
||||
raise ValueError("Guardrail failed words - `litellm` detected")
|
||||
|
||||
async def async_post_call_success_hook(
|
||||
self,
|
||||
data: dict,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
response,
|
||||
):
|
||||
"""
|
||||
Runs on response from LLM API call
|
||||
|
||||
It can be used to reject a response
|
||||
|
||||
If a response contains the word "coffee" -> we will raise an exception
|
||||
"""
|
||||
verbose_proxy_logger.debug("async_pre_call_hook response: %s", response)
|
||||
if isinstance(response, litellm.ModelResponse):
|
||||
for choice in response.choices:
|
||||
if isinstance(choice, litellm.Choices):
|
||||
verbose_proxy_logger.debug("async_pre_call_hook choice: %s", choice)
|
||||
if (
|
||||
choice.message.content
|
||||
and isinstance(choice.message.content, str)
|
||||
and "coffee" in choice.message.content
|
||||
):
|
||||
raise ValueError("Guardrail failed Coffee Detected")
|
||||
|
||||
async def async_post_call_streaming_iterator_hook(
|
||||
self,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
response: Any,
|
||||
request_data: dict,
|
||||
) -> AsyncGenerator[ModelResponseStream, None]:
|
||||
"""
|
||||
Passes the entire stream to the guardrail
|
||||
|
||||
This is useful for guardrails that need to see the entire response, such as PII masking.
|
||||
|
||||
See Aim guardrail implementation for an example - https://github.com/BerriAI/litellm/blob/d0e022cfacb8e9ebc5409bb652059b6fd97b45c0/litellm/proxy/guardrails/guardrail_hooks/aim.py#L168
|
||||
|
||||
Triggered by mode: 'post_call'
|
||||
"""
|
||||
async for item in response:
|
||||
yield item
|
||||
|
||||
```
|
||||
|
||||
## **CustomGuardrail methods**
|
||||
|
||||
| Component | Description | Optional | Checked Data | Can Modify Input | Can Modify Output | Can Fail Call |
|
||||
|-----------|-------------|----------|--------------|------------------|-------------------|----------------|
|
||||
| `apply_guardrail` | Simple method to check and optionally modify text | ✅ | INPUT or OUTPUT | ✅ | ✅ | ✅ |
|
||||
| `async_pre_call_hook` | A hook that runs before the LLM API call | ✅ | INPUT | ✅ | ❌ | ✅ |
|
||||
| `async_moderation_hook` | A hook that runs during the LLM API call| ✅ | INPUT | ❌ | ❌ | ✅ |
|
||||
| `async_post_call_success_hook` | A hook that runs after a successful LLM API call| ✅ | INPUT, OUTPUT | ❌ | ✅ | ✅ |
|
||||
| `async_post_call_streaming_iterator_hook` | A hook that processes streaming responses | ✅ | OUTPUT | ❌ | ✅ | ✅ |
|
||||
|
||||
|
||||
## Frequently Asked Questions
|
||||
|
||||
**Q. Is `apply_guardrail` relevant both in the request and in the response (pre_call, during_call and post_call hooks)?**
|
||||
|
||||
**A.** Yes, one function works in both - See implementation [here](https://github.com/BerriAI/litellm/blob/0292b84dc47473ddeff29bd5a86f529bc523034b/litellm/proxy/utils.py#L825)
|
||||
|
||||
**Q. What do I get in the inputs of `apply_guardrail`? What does each field represent (what is text, language, entities, request_data)?**
|
||||
|
||||
**A.** The main one you should care about is 'text' - this is what you'll want to send to your api for verification - See implementation [here](https://github.com/BerriAI/litellm/blob/0292b84dc47473ddeff29bd5a86f529bc523034b/litellm/llms/anthropic/chat/guardrail_translation/handler.py#L102)
|
||||
|
||||
**Q. Is this function agnostic to the LLM provider? Meaning does it pass the same values for OpenAI and Anthropic for example?
|
||||
|
||||
**A.** Yes
|
||||
|
||||
**Q. How do I know if my guardrail is running?**
|
||||
|
||||
**A.** If you implement `apply_guardrail`, you can query the guardrail directly via [the `/apply_guardrail` API](../../apply_guardrail).
|
||||
|
|
@ -142,8 +142,8 @@ Provides the strongest enforcement by inspecting both prompts and responses.
|
|||
|---------------------------------------|-----------------|-------------|
|
||||
| `api_key` | string | Gray Swan Cygnal API key. Reads from `GRAYSWAN_API_KEY` if omitted. |
|
||||
| `mode` | string or list | Guardrail stages (`pre_call`, `during_call`, `post_call`). |
|
||||
| `optional_params.on_flagged_action` | string | `monitor` (log only) or `block` (raise `HTTPException`). |
|
||||
| `optional_params.on_flagged_action` | string | `monitor` (log only), `block` (raise `HTTPException`), or `passthrough` (include detection info in response without blocking). |
|
||||
| `.optional_params.violation_threshold`| number (0-1) | Scores at or above this value are considered violations. |
|
||||
| `optional_params.reasoning_mode` | string | `off`, `hybrid`, or `thinking`. Enables Cygnal’s reasoning capabilities. |
|
||||
| `optional_params.reasoning_mode` | string | `off`, `hybrid`, or `thinking`. Enables Cygnal's reasoning capabilities. |
|
||||
| `optional_params.categories` | object | Map of custom category names to descriptions. |
|
||||
| `optional_params.policy_id` | string | Gray Swan policy identifier. |
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
These go under `optional_params`:
|
||||
|
||||
- `detector_params` - dict - Parameters to pass to your detector
|
||||
- `extra_headers` - dict - Additional headers to inject into requests to IBM Guardrails, as a key-value dict.
|
||||
- `score_threshold` - float - Only count detections above this score (0.0 to 1.0)
|
||||
- `block_on_detection` - bool - Block the request when violations found. Default: `true`
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,43 @@ guardrails:
|
|||
- `pre_call` Run **before** LLM call, on **input**
|
||||
- `post_call` Run **after** LLM call, on **input & output**
|
||||
|
||||
### `on_disallowed_action` behavior
|
||||
|
||||
| Value | What happens |
|
||||
| --- | --- |
|
||||
| `block` | The request is immediately rejected. Pre-call checks raise a `400` HTTP error. Post-call checks raise `GuardrailRaisedException`, so the proxy responds with an error instead of the model output. Use when invoking the forbidden tool must halt the workflow. |
|
||||
| `rewrite` | LiteLLM silently strips disallowed tools from the payload before it reaches the model (pre-call) or rewrites the model response/tool calls after the fact. The guardrail inserts error text into `message.content`/`tool_result` entries so the client learns the tool was blocked while the rest of the completion continues. Use when you want graceful degradation instead of hard failures. |
|
||||
|
||||
### Custom denial message
|
||||
|
||||
Set `violation_message_template` when you want the guardrail to return a branded error (e.g., “this violates our org policy…”). LiteLLM replaces placeholders from the denied tool:
|
||||
|
||||
- `{tool_name}` – the tool/function name (e.g., `Read`)
|
||||
- `{rule_id}` – the matching rule ID (or `None` when the default action kicks in)
|
||||
- `{default_message}` – the original LiteLLM message if you need to append it
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
guardrails:
|
||||
- guardrail_name: "tool-permission-guardrail"
|
||||
litellm_params:
|
||||
guardrail: tool_permission
|
||||
mode: "post_call"
|
||||
violation_message_template: "this violates our org policy, we don't support executing {tool_name} commands"
|
||||
rules:
|
||||
- id: "allow_bash"
|
||||
tool_name: "Bash"
|
||||
decision: "allow"
|
||||
- id: "deny_read"
|
||||
tool_name: "Read"
|
||||
decision: "deny"
|
||||
default_action: "deny"
|
||||
on_disallowed_action: "block"
|
||||
```
|
||||
|
||||
If a request tries to invoke `Read`, the proxy now returns “this violates our org policy, we don't support executing Read commands” instead of the stock error text. Omit the field to keep the default messaging.
|
||||
|
||||
### 2. Start the Proxy
|
||||
|
||||
```shell
|
||||
|
|
@ -57,7 +94,7 @@ litellm --config config.yaml --port 4000
|
|||
<Tabs>
|
||||
<TabItem value="block" label="Block Request">
|
||||
|
||||
**Block requset**
|
||||
**Block request (`on_disallowed_action: block`)**
|
||||
|
||||
```bash
|
||||
# Test
|
||||
|
|
@ -96,7 +133,7 @@ curl -X POST "http://localhost:4000/v1/chat/completions" \
|
|||
</TabItem>
|
||||
<TabItem value="rewrite" label="Rewrite Request">
|
||||
|
||||
**Rewrite requset**
|
||||
**Rewrite request (`on_disallowed_action: rewrite`)**
|
||||
|
||||
```bash
|
||||
# Test
|
||||
|
|
@ -118,7 +155,7 @@ curl -X POST "http://localhost:4000/v1/chat/completions" \
|
|||
}'
|
||||
```
|
||||
|
||||
**Expected response:**
|
||||
**Expected response (tool removed, completion continues):**
|
||||
|
||||
```json
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Available via the `litellm[proxy]` package or any `litellm` docker image.
|
|||
| Proxy | ✅ | |
|
||||
| SDK | ❌ | Requires postgres DB for storing file ids. |
|
||||
| Available across all providers | ✅ | |
|
||||
| Supported endpoints | `/chat/completions`, `/batch`, `/fine_tuning` | |
|
||||
| Supported endpoints | `/chat/completions`, `/batch`, `/fine_tuning`, `/responses` | |
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -424,4 +424,4 @@ No, as of `v1.71.2` users can only view/edit/delete files they have created.
|
|||
## See Also
|
||||
|
||||
- [Managed Files w/ Finetuning APIs](../../docs/proxy/managed_finetuning)
|
||||
- [Managed Files w/ Batch APIs](../../docs/proxy/managed_batch)
|
||||
- [Managed Files w/ Batch APIs](../../docs/proxy/managed_batches)
|
||||
|
|
@ -260,4 +260,15 @@ print(f"status: {status}")
|
|||
|
||||
When a `target_model_names` is specified, the file is written to all deployments that match the `target_model_names`.
|
||||
|
||||
No additional infrastructure is required.
|
||||
No additional infrastructure is required.
|
||||
|
||||
## Could the batch be created at the eastus-01 deployment but a subsequent get of the batch could be routed to (a different) eastus2-01 deployment ?
|
||||
|
||||
**A.** You can loadbalance b/w multiple models for the initial create batch. Once that's created - we return a file id, which encodes the model deployment used, so it's sticky and only sends any get/delete to that deployment.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,26 @@ For an indepth guide, see [CLI Authentication](./cli_sso).
|
|||
|
||||
:::
|
||||
|
||||
### Prerequisites
|
||||
|
||||
:::warning[Beta Feature - Required Environment Variable]
|
||||
|
||||
CLI SSO Authentication is currently in beta. You must set this environment variable **when starting up your LiteLLM Proxy**:
|
||||
|
||||
```bash
|
||||
export EXPERIMENTAL_UI_LOGIN="True"
|
||||
litellm --config config.yaml
|
||||
```
|
||||
|
||||
Or add it to your proxy startup command:
|
||||
|
||||
```bash
|
||||
EXPERIMENTAL_UI_LOGIN="True" litellm --config config.yaml
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Steps
|
||||
|
||||
1. **Set up the proxy URL**
|
||||
|
||||
|
|
|
|||
193
docs/my-website/docs/proxy/model_compare_ui.md
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Model Compare Playground UI
|
||||
|
||||
Compare multiple LLM models side-by-side in an interactive playground interface. Evaluate model responses, performance metrics, and costs to make informed decisions about which models work best for your use case.
|
||||
|
||||
This feature is **available in v1.80.0-stable and above**.
|
||||
|
||||
## Overview
|
||||
|
||||
The Model Compare Playground UI enables side-by-side comparison of up to 3 different LLM models simultaneously. Configure models, parameters, and test prompts to evaluate and compare model responses with detailed metrics including latency, token usage, and cost.
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_overview.png')} />
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Accessing the Model Compare UI
|
||||
|
||||
#### 1. Navigate to the Playground
|
||||
|
||||
Go to the Playground page in the Admin UI (`PROXY_BASE_URL/ui/?login=success&page=llm-playground`)
|
||||
|
||||
<Image img={require('../../img/ui_playground_navigation.png')} />
|
||||
|
||||
#### 2. Switch to Compare Tab
|
||||
|
||||
Click on the **Compare** tab in the Playground interface.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Setting Up Models
|
||||
|
||||
#### 1. Select Models to Compare
|
||||
|
||||
You can compare up to 3 models simultaneously. For each comparison panel:
|
||||
|
||||
- Click on the model dropdown to see available models
|
||||
- Select a model from your configured endpoints
|
||||
- Models are loaded from your LiteLLM proxy configuration
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_select_models.png')} />
|
||||
|
||||
#### 2. Configure Model Parameters
|
||||
|
||||
Each model panel supports individual parameter configuration:
|
||||
|
||||
**Basic Parameters:**
|
||||
|
||||
- **Temperature**: Controls randomness (0.0 to 2.0)
|
||||
- **Max Tokens**: Maximum tokens in the response
|
||||
|
||||
**Advanced Parameters:**
|
||||
|
||||
- Enable "Use Advanced Params" to configure additional model-specific parameters
|
||||
- Supports all parameters available for the selected model/provider
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_model_parameters.png')} />
|
||||
|
||||
#### 3. Apply Parameters Across Models
|
||||
|
||||
Use the "Sync Settings Across Models" toggle to synchronize parameters (tags, guardrails, temperature, max tokens, etc.) across all comparison panels for consistent testing.
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_sync_across_models.png')} />
|
||||
|
||||
### Guardrails
|
||||
|
||||
Configure and test guardrails directly in the playground:
|
||||
|
||||
1. Click on the guardrails selector in a model panel
|
||||
2. Select one or more guardrails from your configured list
|
||||
3. Test how different models respond to guardrail filtering
|
||||
4. Compare guardrail behavior across models
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_guardrails_config.png')} />
|
||||
|
||||
### Tags
|
||||
|
||||
Apply tags to organize and filter your comparisons:
|
||||
|
||||
1. Select tags from the tag dropdown
|
||||
2. Tags help categorize and track different test scenarios
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_tags_config.png')} />
|
||||
|
||||
### Vector Stores
|
||||
|
||||
Configure vector store retrieval for RAG (Retrieval Augmented Generation) comparisons:
|
||||
|
||||
1. Select vector stores from the dropdown
|
||||
2. Compare how different models utilize retrieved context
|
||||
3. Evaluate RAG performance across models
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_vector_stores_config.png')} />
|
||||
|
||||
## Running Comparisons
|
||||
|
||||
### 1. Enter Your Prompt
|
||||
|
||||
Type your test prompt in the message input area. You can:
|
||||
|
||||
- Enter a single message for all models
|
||||
- Use suggested prompts for quick testing
|
||||
- Build multi-turn conversations
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_enter_prompt.png')} />
|
||||
|
||||
### 2. Send Request
|
||||
|
||||
Click the send button (or press Enter) to start the comparison. All selected models will process the request simultaneously.
|
||||
|
||||
### 3. View Responses
|
||||
|
||||
Responses appear side-by-side in each model panel, making it easy to compare:
|
||||
|
||||
- Response quality and content
|
||||
- Response length and structure
|
||||
- Model-specific formatting
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_responses.png')} />
|
||||
|
||||
## Comparison Metrics
|
||||
|
||||
Each comparison panel displays detailed metrics to help you evaluate model performance:
|
||||
|
||||
### Time To First Token (TTFT)
|
||||
|
||||
Measures the latency from request submission to the first token received. Lower values indicate faster initial response times.
|
||||
|
||||
### Token Usage
|
||||
|
||||
- **Input Tokens**: Number of tokens in the prompt/request
|
||||
- **Output Tokens**: Number of tokens in the model's response
|
||||
- **Reasoning Tokens**: Tokens used for reasoning (if applicable, e.g., o1 models)
|
||||
|
||||
### Total Latency
|
||||
|
||||
Complete time from request to final response, including streaming time.
|
||||
|
||||
### Cost
|
||||
|
||||
If cost tracking is enabled in your LiteLLM configuration, you'll see:
|
||||
|
||||
- Cost per request
|
||||
- Cost breakdown by input/output tokens
|
||||
- Comparison of costs across models
|
||||
|
||||
<Image img={require('../../img/ui_model_compare_cost_metrics.png')} />
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Model Selection
|
||||
|
||||
Compare multiple models on the same prompt to determine which performs best for your specific use case:
|
||||
|
||||
- Response quality
|
||||
- Response time
|
||||
- Cost efficiency
|
||||
- Token usage
|
||||
|
||||
### Parameter Tuning
|
||||
|
||||
Test different parameter configurations across models to find optimal settings:
|
||||
|
||||
- Temperature variations
|
||||
- Max token limits
|
||||
- Advanced parameter combinations
|
||||
|
||||
### Guardrail Testing
|
||||
|
||||
Evaluate how different models respond to safety filters and guardrails:
|
||||
|
||||
- Filter effectiveness
|
||||
- False positive rates
|
||||
- Model-specific guardrail behavior
|
||||
|
||||
### A/B Testing
|
||||
|
||||
Use tags and multiple comparisons to run structured A/B tests:
|
||||
|
||||
- Compare model versions
|
||||
- Test prompt variations
|
||||
- Evaluate feature rollouts
|
||||
|
||||
---
|
||||
|
||||
## Related Features
|
||||
|
||||
- [Playground Chat UI](./playground.md) - Single model testing interface
|
||||
- [Model Management](./model_management.md) - Configure and manage models
|
||||
- [Guardrails](./guardrails.md) - Set up safety filters
|
||||
- [AI Hub](./ai_hub.md) - Share models and agents with your organization
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import Image from '@theme/IdealImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Model Hub
|
||||
|
||||
Tell developers what models are available on the proxy.
|
||||
|
||||
This feature is **available in v1.74.3-stable and above**.
|
||||
|
||||
## Overview
|
||||
|
||||
Admin can select models to expose on public model hub -> Users can go to the public url (`/ui/model_hub_table`) and see available models.
|
||||
|
||||
<Image img={require('../../img/final_public_model_hub_view.png')} />
|
||||
|
||||
## How to use
|
||||
|
||||
### 1. Go to the Admin UI
|
||||
|
||||
Navigate to the Model Hub page in the Admin UI (`PROXY_BASE_URL/ui/?login=success&page=model-hub-table`)
|
||||
|
||||
<Image img={require('../../img/model_hub_admin_view.png')} />
|
||||
|
||||
### 2. Select the models you want to expose
|
||||
|
||||
Click on `Make Public` and select the models you want to expose.
|
||||
|
||||
<Image img={require('../../img/make_public_modal.png')} />
|
||||
|
||||
### 3. Confirm the changes
|
||||
|
||||
<Image img={require('../../img/make_public_modal_confirmation.png')} />
|
||||
|
||||
### 4. Success!
|
||||
|
||||
Go to the public url (`PROXY_BASE_URL/ui/model_hub_table`) and see available models.
|
||||
|
||||
<Image img={require('../../img/final_public_model_hub_view.png')} />
|
||||
|
||||
## API Endpoints
|
||||
|
||||
LiteLLM also exposes REST endpoints:
|
||||
|
||||
- `GET /public/model_hub` – returns the list of public model groups. Requires a valid user API key.
|
||||
- `GET /public/model_hub/info` – returns metadata (docs title, version, useful links) for the public model hub.
|
||||
- `GET /public/providers` – returns a sorted list of all providers supported by LiteLLM. No authentication required.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
curl -s PROXY_BASE_URL/public/providers | jq
|
||||
```
|
||||
|
|
@ -14,11 +14,12 @@ If you're using the LiteLLM CLI with `litellm --config proxy_config.yaml` then y
|
|||
Add this to your proxy config.yaml
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-4o
|
||||
- model_name: gpt-4o
|
||||
litellm_params:
|
||||
model: gpt-4o
|
||||
litellm_settings:
|
||||
callbacks: ["prometheus"]
|
||||
callbacks:
|
||||
- prometheus
|
||||
```
|
||||
|
||||
Start the proxy
|
||||
|
|
|
|||
|
|
@ -59,11 +59,13 @@ Allow others to create/delete their own keys.
|
|||
The Admin UI provides comprehensive model management capabilities:
|
||||
|
||||
- **Add Models**: Add new models through the UI without restarting the proxy
|
||||
- **Model Hub**: Make models public for developers to discover available models
|
||||
- **AI Hub**: Make models and agents public for developers to discover what's available
|
||||
- **Price Data Sync**: Keep model pricing data up to date by syncing from GitHub
|
||||
|
||||
For detailed information on model management, see [Model Management](./model_management.md).
|
||||
|
||||
For information on sharing models and agents, see [AI Hub](./ai_hub.md).
|
||||
|
||||
:::tip Sync Model Pricing Data
|
||||
[Sync model pricing data from GitHub](./sync_models_github.md) to keep your model cost information current.
|
||||
:::
|
||||
|
|
|
|||
|
|
@ -76,8 +76,6 @@ Set `SPEND_LOG_CLEANUP_BATCH_SIZE` to control how many logs are deleted per batc
|
|||
For detailed architecture and how it works, see [Spend Logs Deletion](../proxy/spend_logs_deletion).
|
||||
|
||||
|
||||
## What gets logged?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Here's a schema](https://github.com/BerriAI/litellm/blob/1cdd4065a645021aea931afb9494e7694b4ec64b/schema.prisma#L285) breakdown of what gets logged.
|
||||
|
|
|
|||
|
|
@ -110,3 +110,57 @@ The `primary_secret_name` allows you to read multiple keys from a single AWS Sec
|
|||
|
||||
This reduces the number of AWS Secrets you need to manage.
|
||||
|
||||
## IAM Role Assumption
|
||||
|
||||
Use IAM roles instead of static AWS credentials for better security.
|
||||
|
||||
### Basic IAM Role
|
||||
|
||||
```yaml
|
||||
general_settings:
|
||||
key_management_system: "aws_secret_manager"
|
||||
key_management_settings:
|
||||
store_virtual_keys: true
|
||||
aws_region_name: "us-east-1"
|
||||
aws_role_name: "arn:aws:iam::123456789012:role/LiteLLMSecretManagerRole"
|
||||
aws_session_name: "litellm-session"
|
||||
```
|
||||
|
||||
### Cross-Account Access
|
||||
|
||||
```yaml
|
||||
general_settings:
|
||||
key_management_system: "aws_secret_manager"
|
||||
key_management_settings:
|
||||
store_virtual_keys: true
|
||||
aws_region_name: "us-east-1"
|
||||
aws_role_name: "arn:aws:iam::999999999999:role/CrossAccountRole"
|
||||
aws_external_id: "unique-external-id"
|
||||
```
|
||||
|
||||
### EKS with IRSA
|
||||
|
||||
```yaml
|
||||
general_settings:
|
||||
key_management_system: "aws_secret_manager"
|
||||
key_management_settings:
|
||||
store_virtual_keys: true
|
||||
aws_region_name: "us-east-1"
|
||||
aws_role_name: "arn:aws:iam::123456789012:role/LiteLLMServiceAccountRole"
|
||||
aws_web_identity_token: "os.environ/AWS_WEB_IDENTITY_TOKEN_FILE"
|
||||
```
|
||||
|
||||
### Configuration Parameters
|
||||
|
||||
| Parameter | Description |
|
||||
|-----------|-------------|
|
||||
| `aws_region_name` | AWS region |
|
||||
| `aws_role_name` | IAM role ARN to assume |
|
||||
| `aws_session_name` | Session name (optional) |
|
||||
| `aws_external_id` | External ID for cross-account |
|
||||
| `aws_profile_name` | AWS profile from `~/.aws/credentials` |
|
||||
| `aws_web_identity_token` | OIDC token path for IRSA |
|
||||
| `aws_sts_endpoint` | Custom STS endpoint for VPC |
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ LITELLM_MASTER_KEY gives claude access to all proxy models, whereas a virtual ke
|
|||
Alternatively, use the Anthropic pass-through endpoint:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_BASE_URL="http://0.0.0.0:4000"
|
||||
export ANTHROPIC_BASE_URL="http://0.0.0.0:4000/anthropic"
|
||||
export ANTHROPIC_AUTH_TOKEN="$LITELLM_MASTER_KEY"
|
||||
```
|
||||
|
||||
|
|
@ -221,7 +221,6 @@ You can also connect MCP servers to Claude Code via LiteLLM Proxy.
|
|||
Limitations:
|
||||
|
||||
- Currently, only HTTP MCP servers are supported
|
||||
- Does not work in Cursor IDE yet.
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# /vector_stores/{vector_store_id}/files
|
||||
# /vector_stores/\{vector_store_id\}/files
|
||||
|
||||
Vector store files represent the individual files that live inside a vector store.
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ Vector store support currently works **only with OpenAI vector stores and OpenAI
|
|||
|
||||
## Create vector store file
|
||||
|
||||
`POST http://localhost:4000/v1/vector_stores/{vector_store_id}/files`
|
||||
<code>POST http://localhost:4000/v1/vector_stores/{vector_store_id}/files</code>
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
|
@ -53,7 +53,7 @@ print(vector_store_file)
|
|||
|
||||
## List vector store files
|
||||
|
||||
`GET http://localhost:4000/v1/vector_stores/{vector_store_id}/files`
|
||||
<code>GET http://localhost:4000/v1/vector_stores/{vector_store_id}/files</code>
|
||||
|
||||
Parameters:
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ print(vector_store_files)
|
|||
|
||||
## Retrieve vector store file
|
||||
|
||||
`GET http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}`
|
||||
<code>GET http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}</code>
|
||||
|
||||
```python
|
||||
vector_store_file = client.vector_stores.files.retrieve(
|
||||
|
|
@ -84,7 +84,7 @@ print(vector_store_file)
|
|||
|
||||
## Delete vector store file
|
||||
|
||||
`DELETE http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}`
|
||||
<code>DELETE http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}</code>
|
||||
|
||||
```python
|
||||
deleted_vector_store_file = client.vector_stores.files.delete(
|
||||
|
|
@ -101,14 +101,14 @@ When you need raw content chunks or attribute updates, call the LiteLLM Proxy di
|
|||
### Retrieve file content
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}/content" \
|
||||
curl -X GET "http://localhost:4000/v1/vector_stores/\{vector_store_id\}/files/\{file_id\}/content" \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
```
|
||||
|
||||
### Update file attributes
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:4000/v1/vector_stores/{vector_store_id}/files/{file_id}" \
|
||||
curl -X POST "http://localhost:4000/v1/vector_stores/\{vector_store_id\}/files/\{file_id\}" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
|
|
|
|||
|
|
@ -101,6 +101,21 @@ const config = {
|
|||
include: ['**/*.{md,mdx}'],
|
||||
},
|
||||
],
|
||||
[
|
||||
'@docusaurus/plugin-content-blog',
|
||||
{
|
||||
id: 'blog',
|
||||
path: './blog',
|
||||
routeBasePath: 'blog',
|
||||
blogTitle: 'Blog',
|
||||
blogSidebarTitle: 'All Posts',
|
||||
blogSidebarCount: 'ALL',
|
||||
postsPerPage: 10,
|
||||
showReadingTime: false,
|
||||
sortPosts: 'descending',
|
||||
include: ['**/index.{md,mdx}'],
|
||||
},
|
||||
],
|
||||
|
||||
() => ({
|
||||
name: 'cripchat',
|
||||
|
|
@ -129,6 +144,7 @@ const config = {
|
|||
docs: {
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
},
|
||||
blog: false, // Disable the default blog plugin from preset-classic
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
|
|
@ -177,6 +193,7 @@ const config = {
|
|||
to: "docs/enterprise"
|
||||
},
|
||||
{ to: '/release_notes', label: 'Release Notes', position: 'left' },
|
||||
{ to: '/blog', label: 'Blog', position: 'left' },
|
||||
{
|
||||
href: 'https://models.litellm.ai/',
|
||||
label: '💸 LLM Model Cost Map',
|
||||
|
|
@ -231,6 +248,11 @@ const config = {
|
|||
],
|
||||
copyright: `Copyright © ${new Date().getFullYear()} liteLLM`,
|
||||
},
|
||||
colorMode: {
|
||||
defaultMode: 'light',
|
||||
disableSwitch: false,
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
|
|
|
|||
BIN
docs/my-website/img/add_agent.png
Normal file
|
After Width: | Height: | Size: 616 KiB |
BIN
docs/my-website/img/agent_hub_clean.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
docs/my-website/img/ai_hub_with_agents.png
Normal file
|
After Width: | Height: | Size: 626 KiB |
BIN
docs/my-website/img/app_role2.png
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
docs/my-website/img/app_role3.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
docs/my-website/img/app_roles.png
Normal file
|
After Width: | Height: | Size: 277 KiB |
BIN
docs/my-website/img/favicon_converted.ico
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
docs/my-website/img/make_agents_public.png
Normal file
|
After Width: | Height: | Size: 647 KiB |
BIN
docs/my-website/img/model_compare_overview.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
docs/my-website/img/public_agent_hub.png
Normal file
|
After Width: | Height: | Size: 445 KiB |
BIN
docs/my-website/img/ui_model_compare_cost_metrics.png
Normal file
|
After Width: | Height: | Size: 538 KiB |
BIN
docs/my-website/img/ui_model_compare_enter_prompt.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/my-website/img/ui_model_compare_guardrails_config.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
docs/my-website/img/ui_model_compare_model_parameters.png
Normal file
|
After Width: | Height: | Size: 291 KiB |
BIN
docs/my-website/img/ui_model_compare_overview.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
docs/my-website/img/ui_model_compare_responses.png
Normal file
|
After Width: | Height: | Size: 469 KiB |
BIN
docs/my-website/img/ui_model_compare_select_model.png
Normal file
|
After Width: | Height: | Size: 253 KiB |
BIN
docs/my-website/img/ui_model_compare_sync_across_models.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
docs/my-website/img/ui_model_compare_tags_config.png
Normal file
|
After Width: | Height: | Size: 373 KiB |
BIN
docs/my-website/img/ui_model_compare_vector_stores_config.png
Normal file
|
After Width: | Height: | Size: 392 KiB |
BIN
docs/my-website/img/ui_playground_navigation.png
Normal file
|
After Width: | Height: | Size: 331 KiB |
47
docs/my-website/package-lock.json
generated
|
|
@ -11378,9 +11378,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.253",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.253.tgz",
|
||||
"integrity": "sha512-O0tpQ/35rrgdiGQ0/OFWhy1itmd9A6TY9uQzlqj3hKSu/aYpe7UIn5d7CU2N9myH6biZiWF3VMZVuup8pw5U9w==",
|
||||
"version": "1.5.254",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.254.tgz",
|
||||
"integrity": "sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
|
@ -11601,6 +11601,19 @@
|
|||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esrecurse": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
|
|
@ -12619,6 +12632,28 @@
|
|||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gray-matter/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/gray-matter/node_modules/js-yaml": {
|
||||
"version": "3.14.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/gzip-size": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz",
|
||||
|
|
@ -21028,6 +21063,12 @@
|
|||
"wbuf": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/srcset": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -48,10 +48,16 @@
|
|||
"node": ">=16.14",
|
||||
"npm": ">=8.3.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"webpack-dev-server": ">=5.2.1",
|
||||
"form-data": ">=4.0.4",
|
||||
"mermaid": ">=11.10.0",
|
||||
"gray-matter": "4.0.3"
|
||||
},
|
||||
"overrides": {
|
||||
"webpack-dev-server": ">=5.2.1",
|
||||
"form-data": ">=4.0.4",
|
||||
"mermaid": ">=11.10.0",
|
||||
"js-yaml": ">=4.1.1"
|
||||
"gray-matter": "4.0.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
18
docs/my-website/release_notes/authors.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
krrish:
|
||||
name: Krrish Dholakia
|
||||
title: CEO, LiteLLM
|
||||
url: https://www.linkedin.com/in/krish-d/
|
||||
image_url: https://pbs.twimg.com/profile_images/1298587542745358340/DZv3Oj-h_400x400.jpg
|
||||
|
||||
ishaan:
|
||||
name: Ishaan Jaffer
|
||||
title: CTO, LiteLLM
|
||||
url: https://www.linkedin.com/in/reffajnaahsi/
|
||||
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
|
||||
|
||||
# Alias for typo in name
|
||||
ishaan-alt:
|
||||
name: Ishaan Jaff
|
||||
title: CTO, LiteLLM
|
||||
url: https://www.linkedin.com/in/reffajnaahsi/
|
||||
image_url: https://pbs.twimg.com/profile_images/1613813310264340481/lz54oEiB_400x400.jpg
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: "v1.80.0-stable - RunwayML Provider Support"
|
||||
title: "[Preview] v1.80.0-stable - Agent Hub Support"
|
||||
slug: "v1-80-0"
|
||||
date: 2025-11-15T10:00:00
|
||||
authors:
|
||||
|
|
@ -27,7 +27,7 @@ import TabItem from '@theme/TabItem';
|
|||
docker run \
|
||||
-e STORE_MODEL_IN_DB=True \
|
||||
-p 4000:4000 \
|
||||
ghcr.io/berriai/litellm:v1.80.0.rc.1
|
||||
ghcr.io/berriai/litellm:v1.80.0.rc.2
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -45,7 +45,8 @@ pip install litellm==1.80.0
|
|||
|
||||
## Key Highlights
|
||||
|
||||
- **🆕 RunwayML Provider** - Complete video generation, image generation, and text-to-speech support
|
||||
- **🆕 Agent Hub Support** - Register and make agents public for your organization
|
||||
- **RunwayML Provider** - Complete video generation, image generation, and text-to-speech support
|
||||
- **GPT-5.1 Family Support** - Day-0 support for OpenAI's latest GPT-5.1 and GPT-5.1-Codex models
|
||||
- **Prometheus OSS** - Prometheus metrics now available in open-source version
|
||||
- **Vector Store Files API** - Complete OpenAI-compatible Vector Store Files API with full CRUD operations
|
||||
|
|
@ -53,6 +54,46 @@ pip install litellm==1.80.0
|
|||
|
||||
---
|
||||
|
||||
### Agent Hub
|
||||
|
||||
<Image img={require('../../img/agent_hub_clean.png')} />
|
||||
|
||||
This release adds support for registering and making agents public for your organization. This is great for **Proxy Admins** who want a central place to make agents built in their organization, discoverable to their users.
|
||||
|
||||
Here's the flow:
|
||||
1. Add agent to litellm.
|
||||
2. Make it public.
|
||||
3. Allow anyone to discover it on the public AI Hub page.
|
||||
|
||||
[**Get Started with Agent Hub**](../../docs/proxy/ai_hub)
|
||||
|
||||
|
||||
### Performance – `/embeddings` 13× Lower p95 Latency
|
||||
|
||||
This update significantly improves `/embeddings` latency by routing it through the same optimized pipeline as `/chat/completions`, benefiting from all previously applied networking optimizations.
|
||||
|
||||
### Results
|
||||
|
||||
| Metric | Before | After | Improvement |
|
||||
| --- | --- | --- | --- |
|
||||
| p95 latency | 5,700 ms | **430 ms** | −92% (~13× faster)** |
|
||||
| p99 latency | 7,200 ms | **780 ms** | −89% |
|
||||
| Average latency | 844 ms | **262 ms** | −69% |
|
||||
| Median latency | 290 ms | **230 ms** | −21% |
|
||||
| RPS | 1,216.7 | **1,219.7** | **+0.25%** |
|
||||
|
||||
### Test Setup
|
||||
|
||||
| Category | Specification |
|
||||
| --- | --- |
|
||||
| **Load Testing** | Locust: 1,000 concurrent users, 500 ramp-up |
|
||||
| **System** | 4 vCPUs, 8 GB RAM, 4 workers, 4 instances |
|
||||
| **Database** | PostgreSQL (Redis unused) |
|
||||
| **Configuration** | [config.yaml](https://gist.github.com/AlexsanderHamir/550791675fd752befcac6a9e44024652) |
|
||||
| **Load Script** | [no_cache_hits.py](https://gist.github.com/AlexsanderHamir/99d673bf74cdd81fd39f59fa9048f2e8) |
|
||||
|
||||
---
|
||||
|
||||
### 🆕 RunwayML
|
||||
|
||||
Complete integration for RunwayML's Gen-4 family of models, supporting video generation, image generation, and text-to-speech.
|
||||
|
|
@ -97,7 +138,7 @@ litellm_settings:
|
|||
|
||||
---
|
||||
|
||||
### Vector Store Files API - Stable Release
|
||||
### Vector Store Files API
|
||||
|
||||
Complete OpenAI-compatible Vector Store Files API now stable, enabling full file lifecycle management within vector stores.
|
||||
|
||||
|
|
@ -120,7 +161,28 @@ curl --location 'http://localhost:4000/v1/vector_stores/vs_123/files' \
|
|||
}'
|
||||
```
|
||||
|
||||
[Get Started with Vector Stores](../../docs/vector_stores)
|
||||
[Get Started with Vector Stores](../../docs/vector_store_files)
|
||||
|
||||
---
|
||||
|
||||
## New Providers and Endpoints
|
||||
|
||||
### New Providers
|
||||
|
||||
| Provider | Supported Endpoints | Description |
|
||||
| -------- | ------------------- | ----------- |
|
||||
| **[RunwayML](../../docs/providers/runwayml/videos)** | `/v1/videos`, `/v1/images/generations`, `/v1/audio/speech` | Gen-4 video generation, image generation, and text-to-speech |
|
||||
|
||||
### New LLM API Endpoints
|
||||
|
||||
| Endpoint | Method | Description | Documentation |
|
||||
| -------- | ------ | ----------- | ------------- |
|
||||
| `/v1/vector_stores/{vector_store_id}/files` | POST | Create vector store file | [Docs](../../docs/vector_store_files) |
|
||||
| `/v1/vector_stores/{vector_store_id}/files` | GET | List vector store files | [Docs](../../docs/vector_store_files) |
|
||||
| `/v1/vector_stores/{vector_store_id}/files/{file_id}` | GET | Retrieve vector store file | [Docs](../../docs/vector_store_files) |
|
||||
| `/v1/vector_stores/{vector_store_id}/files/{file_id}/content` | GET | Retrieve file content | [Docs](../../docs/vector_store_files) |
|
||||
| `/v1/vector_stores/{vector_store_id}/files/{file_id}` | DELETE | Delete vector store file | [Docs](../../docs/vector_store_files) |
|
||||
| `/v1/vector_stores/{vector_store_id}` | DELETE | Delete vector store | [Docs](../../docs/vector_store_files) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -146,13 +146,14 @@ const sidebars = {
|
|||
type: "category",
|
||||
label: "Admin UI",
|
||||
items: [
|
||||
"proxy/ui",
|
||||
"proxy/admin_ui_sso",
|
||||
"proxy/custom_root_ui",
|
||||
"proxy/custom_sso",
|
||||
"proxy/model_hub",
|
||||
"proxy/ai_hub",
|
||||
"proxy/model_compare_ui",
|
||||
"proxy/public_teams",
|
||||
"proxy/self_serve",
|
||||
"proxy/ui",
|
||||
"proxy/ui/bulk_edit_users",
|
||||
"proxy/ui_credentials",
|
||||
"tutorials/scim_litellm",
|
||||
|
|
@ -530,13 +531,39 @@ const sidebars = {
|
|||
"providers/bedrock_vector_store",
|
||||
]
|
||||
},
|
||||
"providers/milvus_vector_stores",
|
||||
"providers/litellm_proxy",
|
||||
"providers/meta_llama",
|
||||
"providers/mistral",
|
||||
"providers/ai21",
|
||||
"providers/aiml",
|
||||
"providers/aleph_alpha",
|
||||
"providers/anyscale",
|
||||
"providers/baseten",
|
||||
"providers/bytez",
|
||||
"providers/cerebras",
|
||||
"providers/clarifai",
|
||||
"providers/cloudflare_workers",
|
||||
"providers/codestral",
|
||||
"providers/cohere",
|
||||
"providers/anyscale",
|
||||
"providers/cometapi",
|
||||
"providers/compactifai",
|
||||
"providers/custom_llm_server",
|
||||
"providers/dashscope",
|
||||
"providers/databricks",
|
||||
"providers/datarobot",
|
||||
"providers/deepgram",
|
||||
"providers/deepinfra",
|
||||
"providers/deepseek",
|
||||
"providers/docker_model_runner",
|
||||
"providers/elevenlabs",
|
||||
"providers/fal_ai",
|
||||
"providers/featherless_ai",
|
||||
"providers/fireworks_ai",
|
||||
"providers/friendliai",
|
||||
"providers/galadriel",
|
||||
"providers/github",
|
||||
"providers/github_copilot",
|
||||
"providers/gradient_ai",
|
||||
"providers/groq",
|
||||
"providers/heroku",
|
||||
{
|
||||
type: "category",
|
||||
label: "HuggingFace",
|
||||
|
|
@ -546,10 +573,21 @@ const sidebars = {
|
|||
]
|
||||
},
|
||||
"providers/hyperbolic",
|
||||
"providers/databricks",
|
||||
"providers/deepgram",
|
||||
"providers/watsonx",
|
||||
"providers/predibase",
|
||||
"providers/infinity",
|
||||
"providers/jina_ai",
|
||||
"providers/lambda_ai",
|
||||
"providers/lemonade",
|
||||
"providers/llamafile",
|
||||
"providers/lm_studio",
|
||||
"providers/meta_llama",
|
||||
"providers/milvus_vector_stores",
|
||||
"providers/mistral",
|
||||
"providers/moonshot",
|
||||
"providers/morph",
|
||||
"providers/nebius",
|
||||
"providers/nlp_cloud",
|
||||
"providers/novita",
|
||||
{ type: "doc", id: "providers/nscale", label: "Nscale (EU Sovereign)" },
|
||||
{
|
||||
type: "category",
|
||||
label: "Nvidia NIM",
|
||||
|
|
@ -558,37 +596,13 @@ const sidebars = {
|
|||
"providers/nvidia_nim_rerank",
|
||||
]
|
||||
},
|
||||
{ type: "doc", id: "providers/nscale", label: "Nscale (EU Sovereign)" },
|
||||
"providers/xai",
|
||||
"providers/moonshot",
|
||||
"providers/lm_studio",
|
||||
"providers/cerebras",
|
||||
"providers/volcano",
|
||||
"providers/triton-inference-server",
|
||||
"providers/oci",
|
||||
"providers/ollama",
|
||||
"providers/openrouter",
|
||||
"providers/ovhcloud",
|
||||
"providers/perplexity",
|
||||
"providers/friendliai",
|
||||
"providers/galadriel",
|
||||
"providers/topaz",
|
||||
"providers/groq",
|
||||
"providers/deepseek",
|
||||
"providers/elevenlabs",
|
||||
"providers/fal_ai",
|
||||
"providers/fireworks_ai",
|
||||
"providers/clarifai",
|
||||
"providers/compactifai",
|
||||
"providers/lemonade",
|
||||
"providers/vllm",
|
||||
"providers/llamafile",
|
||||
"providers/infinity",
|
||||
"providers/xinference",
|
||||
"providers/aiml",
|
||||
"providers/cloudflare_workers",
|
||||
"providers/deepinfra",
|
||||
"providers/github",
|
||||
"providers/github_copilot",
|
||||
"providers/ai21",
|
||||
"providers/nlp_cloud",
|
||||
"providers/petals",
|
||||
"providers/predibase",
|
||||
"providers/recraft",
|
||||
"providers/replicate",
|
||||
{
|
||||
|
|
@ -599,32 +613,20 @@ const sidebars = {
|
|||
"providers/runwayml/videos",
|
||||
]
|
||||
},
|
||||
"providers/sambanova",
|
||||
"providers/snowflake",
|
||||
"providers/togetherai",
|
||||
"providers/topaz",
|
||||
"providers/triton-inference-server",
|
||||
"providers/v0",
|
||||
"providers/vercel_ai_gateway",
|
||||
"providers/morph",
|
||||
"providers/lambda_ai",
|
||||
"providers/novita",
|
||||
"providers/vllm",
|
||||
"providers/volcano",
|
||||
"providers/voyage",
|
||||
"providers/jina_ai",
|
||||
"providers/aleph_alpha",
|
||||
"providers/baseten",
|
||||
"providers/openrouter",
|
||||
"providers/sambanova",
|
||||
"providers/custom_llm_server",
|
||||
"providers/petals",
|
||||
"providers/snowflake",
|
||||
"providers/gradient_ai",
|
||||
"providers/featherless_ai",
|
||||
"providers/nebius",
|
||||
"providers/dashscope",
|
||||
"providers/bytez",
|
||||
"providers/heroku",
|
||||
"providers/oci",
|
||||
"providers/datarobot",
|
||||
"providers/ovhcloud",
|
||||
"providers/wandb_inference",
|
||||
"providers/cometapi",
|
||||
"providers/watsonx",
|
||||
"providers/xai",
|
||||
"providers/xinference",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 6.2 KiB |
BIN
enterprise/dist/litellm_enterprise-0.1.22-py3-none-any.whl
vendored
Normal file
BIN
enterprise/dist/litellm_enterprise-0.1.22.tar.gz
vendored
Normal file
|
|
@ -40,7 +40,7 @@ class EnterpriseCallbackControls:
|
|||
#########################################################
|
||||
# premium user check
|
||||
#########################################################
|
||||
if not EnterpriseCallbackControls._premium_user_check():
|
||||
if not EnterpriseCallbackControls._should_allow_dynamic_callback_disabling():
|
||||
return False
|
||||
#########################################################
|
||||
if isinstance(callback, str):
|
||||
|
|
@ -84,8 +84,15 @@ class EnterpriseCallbackControls:
|
|||
return None
|
||||
|
||||
@staticmethod
|
||||
def _premium_user_check():
|
||||
def _should_allow_dynamic_callback_disabling():
|
||||
import litellm
|
||||
from litellm.proxy.proxy_server import premium_user
|
||||
|
||||
# Check if admin has disabled this feature
|
||||
if litellm.allow_dynamic_callback_disabling is not True:
|
||||
verbose_logger.debug("Dynamic callback disabling is disabled by admin via litellm.allow_dynamic_callback_disabling")
|
||||
return False
|
||||
|
||||
if premium_user:
|
||||
return True
|
||||
verbose_logger.warning(f"Disabling callbacks using request headers is an enterprise feature. {CommonProxyErrors.not_premium_user.value}")
|
||||
|
|
|
|||
|
|
@ -296,6 +296,16 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
file_ids, user_api_key_dict.parent_otel_span
|
||||
)
|
||||
|
||||
data["model_file_id_mapping"] = model_file_id_mapping
|
||||
elif call_type == CallTypes.aresponses.value or call_type == CallTypes.responses.value:
|
||||
# Handle managed files in responses API input
|
||||
input_data = data.get("input")
|
||||
if input_data:
|
||||
file_ids = self.get_file_ids_from_responses_input(input_data)
|
||||
if file_ids:
|
||||
model_file_id_mapping = await self.get_model_file_id_mapping(
|
||||
file_ids, user_api_key_dict.parent_otel_span
|
||||
)
|
||||
data["model_file_id_mapping"] = model_file_id_mapping
|
||||
elif call_type == CallTypes.afile_content.value:
|
||||
retrieve_file_id = cast(Optional[str], data.get("file_id"))
|
||||
|
|
@ -453,6 +463,47 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
file_ids.append(file_id)
|
||||
return file_ids
|
||||
|
||||
def get_file_ids_from_responses_input(
|
||||
self, input: Union[str, List[Dict[str, Any]]]
|
||||
) -> List[str]:
|
||||
"""
|
||||
Gets file ids from responses API input.
|
||||
|
||||
The input can be:
|
||||
- A string (no files)
|
||||
- A list of input items, where each item can have:
|
||||
- type: "input_file" with file_id
|
||||
- content: a list that can contain items with type: "input_file" and file_id
|
||||
"""
|
||||
file_ids: List[str] = []
|
||||
|
||||
if isinstance(input, str):
|
||||
return file_ids
|
||||
|
||||
if not isinstance(input, list):
|
||||
return file_ids
|
||||
|
||||
for item in input:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
|
||||
# Check for direct input_file type
|
||||
if item.get("type") == "input_file":
|
||||
file_id = item.get("file_id")
|
||||
if file_id:
|
||||
file_ids.append(file_id)
|
||||
|
||||
# Check for input_file in content array
|
||||
content = item.get("content")
|
||||
if isinstance(content, list):
|
||||
for content_item in content:
|
||||
if isinstance(content_item, dict) and content_item.get("type") == "input_file":
|
||||
file_id = content_item.get("file_id")
|
||||
if file_id:
|
||||
file_ids.append(file_id)
|
||||
|
||||
return file_ids
|
||||
|
||||
async def get_model_file_id_mapping(
|
||||
self, file_ids: List[str], litellm_parent_otel_span: Span
|
||||
) -> dict:
|
||||
|
|
@ -478,7 +529,6 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
for file_id in file_ids:
|
||||
## CHECK IF FILE ID IS MANAGED BY LITELM
|
||||
is_base64_unified_file_id = _is_base64_encoded_unified_file_id(file_id)
|
||||
|
||||
if is_base64_unified_file_id:
|
||||
litellm_managed_file_ids.append(file_id)
|
||||
|
||||
|
|
@ -489,6 +539,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
unified_file_object = await self.get_unified_file_id(
|
||||
file_id, litellm_parent_otel_span
|
||||
)
|
||||
|
||||
if unified_file_object:
|
||||
file_id_mapping[file_id] = unified_file_object.model_mappings
|
||||
|
||||
|
|
@ -764,18 +815,21 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
llm_router: Router,
|
||||
**data: Dict,
|
||||
) -> OpenAIFileObject:
|
||||
file_id = convert_b64_uid_to_unified_uid(file_id)
|
||||
|
||||
# file_id = convert_b64_uid_to_unified_uid(file_id)
|
||||
model_file_id_mapping = await self.get_model_file_id_mapping(
|
||||
[file_id], litellm_parent_otel_span
|
||||
)
|
||||
|
||||
specific_model_file_id_mapping = model_file_id_mapping.get(file_id)
|
||||
if specific_model_file_id_mapping:
|
||||
for model_id, file_id in specific_model_file_id_mapping.items():
|
||||
await llm_router.afile_delete(model=model_id, file_id=file_id, **data) # type: ignore
|
||||
for model_id, model_file_id in specific_model_file_id_mapping.items():
|
||||
await llm_router.afile_delete(model=model_id, file_id=model_file_id, **data) # type: ignore
|
||||
|
||||
stored_file_object = await self.delete_unified_file_id(
|
||||
file_id, litellm_parent_otel_span
|
||||
)
|
||||
|
||||
if stored_file_object:
|
||||
return stored_file_object
|
||||
else:
|
||||
|
|
@ -796,6 +850,7 @@ class _PROXY_LiteLLMManagedFiles(CustomLogger, BaseFileEndpoints):
|
|||
model_file_id_mapping
|
||||
or await self.get_model_file_id_mapping([file_id], litellm_parent_otel_span)
|
||||
)
|
||||
|
||||
specific_model_file_id_mapping = model_file_id_mapping.get(file_id)
|
||||
|
||||
if specific_model_file_id_mapping:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "litellm-enterprise"
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
description = "Package for LiteLLM Enterprise features"
|
||||
authors = ["BerriAI"]
|
||||
readme = "README.md"
|
||||
|
|
@ -22,7 +22,7 @@ requires = ["poetry-core"]
|
|||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.commitizen]
|
||||
version = "0.1.21"
|
||||
version = "0.1.22"
|
||||
version_files = [
|
||||
"pyproject.toml:version",
|
||||
"../requirements.txt:litellm-enterprise==",
|
||||
|
|
|
|||
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.4.6-py3-none-any.whl
vendored
Normal file
BIN
litellm-proxy-extras/dist/litellm_proxy_extras-0.4.6.tar.gz
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
-- This is an empty migration.
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
-- This is an empty migration.
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
-- DropIndex
|
||||
DROP INDEX "LiteLLM_PromptTable_prompt_id_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_PromptTable" ADD COLUMN "version" INTEGER NOT NULL DEFAULT 1;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LiteLLM_PromptTable_prompt_id_idx" ON "LiteLLM_PromptTable"("prompt_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "LiteLLM_PromptTable_prompt_id_version_key" ON "LiteLLM_PromptTable"("prompt_id", "version");
|
||||
|
||||
|
|
@ -561,11 +561,15 @@ model LiteLLM_GuardrailsTable {
|
|||
// Prompt table for storing prompt configurations
|
||||
model LiteLLM_PromptTable {
|
||||
id String @id @default(uuid())
|
||||
prompt_id String @unique
|
||||
prompt_id String
|
||||
version Int @default(1)
|
||||
litellm_params Json
|
||||
prompt_info Json?
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
|
||||
@@unique([prompt_id, version])
|
||||
@@index([prompt_id])
|
||||
}
|
||||
|
||||
model LiteLLM_HealthCheckTable {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "litellm-proxy-extras"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package."
|
||||
authors = ["BerriAI"]
|
||||
readme = "README.md"
|
||||
|
|
@ -22,7 +22,7 @@ requires = ["poetry-core"]
|
|||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.commitizen]
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
version_files = [
|
||||
"pyproject.toml:version",
|
||||
"../requirements.txt:litellm-proxy-extras==",
|
||||
|
|
|
|||
|
|
@ -181,22 +181,22 @@ prometheus_initialize_budget_metrics: Optional[bool] = False
|
|||
require_auth_for_metrics_endpoint: Optional[bool] = False
|
||||
argilla_batch_size: Optional[int] = None
|
||||
datadog_use_v1: Optional[bool] = False # if you want to use v1 datadog logged payload.
|
||||
gcs_pub_sub_use_v1: Optional[
|
||||
bool
|
||||
] = False # if you want to use v1 gcs pubsub logged payload
|
||||
generic_api_use_v1: Optional[
|
||||
bool
|
||||
] = False # if you want to use v1 generic api logged payload
|
||||
gcs_pub_sub_use_v1: Optional[bool] = (
|
||||
False # if you want to use v1 gcs pubsub logged payload
|
||||
)
|
||||
generic_api_use_v1: Optional[bool] = (
|
||||
False # if you want to use v1 generic api logged payload
|
||||
)
|
||||
argilla_transformation_object: Optional[Dict[str, Any]] = None
|
||||
_async_input_callback: List[
|
||||
Union[str, Callable, CustomLogger]
|
||||
] = [] # internal variable - async custom callbacks are routed here.
|
||||
_async_success_callback: List[
|
||||
Union[str, Callable, CustomLogger]
|
||||
] = [] # internal variable - async custom callbacks are routed here.
|
||||
_async_failure_callback: List[
|
||||
Union[str, Callable, CustomLogger]
|
||||
] = [] # internal variable - async custom callbacks are routed here.
|
||||
_async_input_callback: List[Union[str, Callable, CustomLogger]] = (
|
||||
[]
|
||||
) # internal variable - async custom callbacks are routed here.
|
||||
_async_success_callback: List[Union[str, Callable, CustomLogger]] = (
|
||||
[]
|
||||
) # internal variable - async custom callbacks are routed here.
|
||||
_async_failure_callback: List[Union[str, Callable, CustomLogger]] = (
|
||||
[]
|
||||
) # internal variable - async custom callbacks are routed here.
|
||||
pre_call_rules: List[Callable] = []
|
||||
post_call_rules: List[Callable] = []
|
||||
turn_off_message_logging: Optional[bool] = False
|
||||
|
|
@ -204,18 +204,18 @@ log_raw_request_response: bool = False
|
|||
redact_messages_in_exceptions: Optional[bool] = False
|
||||
redact_user_api_key_info: Optional[bool] = False
|
||||
filter_invalid_headers: Optional[bool] = False
|
||||
add_user_information_to_llm_headers: Optional[
|
||||
bool
|
||||
] = None # adds user_id, team_id, token hash (params from StandardLoggingMetadata) to request headers
|
||||
add_user_information_to_llm_headers: Optional[bool] = (
|
||||
None # adds user_id, team_id, token hash (params from StandardLoggingMetadata) to request headers
|
||||
)
|
||||
store_audit_logs = False # Enterprise feature, allow users to see audit logs
|
||||
### end of callbacks #############
|
||||
|
||||
email: Optional[
|
||||
str
|
||||
] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
token: Optional[
|
||||
str
|
||||
] = None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
email: Optional[str] = (
|
||||
None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
)
|
||||
token: Optional[str] = (
|
||||
None # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
)
|
||||
telemetry = True
|
||||
max_tokens: int = DEFAULT_MAX_TOKENS # OpenAI Defaults
|
||||
drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False))
|
||||
|
|
@ -271,9 +271,9 @@ use_client: bool = False
|
|||
ssl_verify: Union[str, bool] = True
|
||||
ssl_security_level: Optional[str] = None
|
||||
ssl_certificate: Optional[str] = None
|
||||
ssl_ecdh_curve: Optional[
|
||||
str
|
||||
] = None # Set to 'X25519' to disable PQC and improve performance
|
||||
ssl_ecdh_curve: Optional[str] = (
|
||||
None # Set to 'X25519' to disable PQC and improve performance
|
||||
)
|
||||
disable_streaming_logging: bool = False
|
||||
disable_token_counter: bool = False
|
||||
disable_add_transform_inline_image_block: bool = False
|
||||
|
|
@ -319,20 +319,24 @@ enable_loadbalancing_on_batch_endpoints: Optional[bool] = None
|
|||
enable_caching_on_provider_specific_optional_params: bool = (
|
||||
False # feature-flag for caching on optional params - e.g. 'top_k'
|
||||
)
|
||||
caching: bool = False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
caching_with_models: bool = False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
cache: Optional[
|
||||
Cache
|
||||
] = None # cache object <- use this - https://docs.litellm.ai/docs/caching
|
||||
caching: bool = (
|
||||
False # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
)
|
||||
caching_with_models: bool = (
|
||||
False # # Not used anymore, will be removed in next MAJOR release - https://github.com/BerriAI/litellm/discussions/648
|
||||
)
|
||||
cache: Optional[Cache] = (
|
||||
None # cache object <- use this - https://docs.litellm.ai/docs/caching
|
||||
)
|
||||
default_in_memory_ttl: Optional[float] = None
|
||||
default_redis_ttl: Optional[float] = None
|
||||
default_redis_batch_cache_expiry: Optional[float] = None
|
||||
model_alias_map: Dict[str, str] = {}
|
||||
model_group_settings: Optional["ModelGroupSettings"] = None
|
||||
max_budget: float = 0.0 # set the max budget across all providers
|
||||
budget_duration: Optional[
|
||||
str
|
||||
] = None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d").
|
||||
budget_duration: Optional[str] = (
|
||||
None # proxy only - resets budget after fixed duration. You can set duration as seconds ("30s"), minutes ("30m"), hours ("30h"), days ("30d").
|
||||
)
|
||||
default_soft_budget: float = (
|
||||
DEFAULT_SOFT_BUDGET # by default all litellm proxy keys have a soft budget of 50.0
|
||||
)
|
||||
|
|
@ -341,7 +345,9 @@ forward_traceparent_to_llm_provider: bool = False
|
|||
|
||||
_current_cost = 0.0 # private variable, used if max budget is set
|
||||
error_logs: Dict = {}
|
||||
add_function_to_prompt: bool = False # if function calling not supported by api, append function call details to system prompt
|
||||
add_function_to_prompt: bool = (
|
||||
False # if function calling not supported by api, append function call details to system prompt
|
||||
)
|
||||
client_session: Optional[httpx.Client] = None
|
||||
aclient_session: Optional[httpx.AsyncClient] = None
|
||||
model_fallbacks: Optional[List] = None # Deprecated for 'litellm.fallbacks'
|
||||
|
|
@ -379,8 +385,12 @@ prometheus_metrics_config: Optional[List] = None
|
|||
disable_add_prefix_to_prompt: bool = (
|
||||
False # used by anthropic, to disable adding prefix to prompt
|
||||
)
|
||||
disable_copilot_system_to_assistant: bool = False # If false (default), converts all 'system' role messages to 'assistant' for GitHub Copilot compatibility. Set to true to disable this behavior.
|
||||
disable_copilot_system_to_assistant: bool = (
|
||||
False # If false (default), converts all 'system' role messages to 'assistant' for GitHub Copilot compatibility. Set to true to disable this behavior.
|
||||
)
|
||||
public_mcp_servers: Optional[List[str]] = None
|
||||
public_model_groups: Optional[List[str]] = None
|
||||
public_agent_groups: Optional[List[str]] = None
|
||||
public_model_groups_links: Dict[str, str] = {}
|
||||
#### REQUEST PRIORITIZATION #######
|
||||
priority_reservation: Optional[Dict[str, Union[float, PriorityReservationDict]]] = None
|
||||
|
|
@ -390,13 +400,17 @@ priority_reservation_settings: "PriorityReservationSettings" = (
|
|||
|
||||
|
||||
######## Networking Settings ########
|
||||
use_aiohttp_transport: bool = True # Older variable, aiohttp is now the default. use disable_aiohttp_transport instead.
|
||||
use_aiohttp_transport: bool = (
|
||||
True # Older variable, aiohttp is now the default. use disable_aiohttp_transport instead.
|
||||
)
|
||||
aiohttp_trust_env: bool = False # set to true to use HTTP_ Proxy settings
|
||||
disable_aiohttp_transport: bool = False # Set this to true to use httpx instead
|
||||
disable_aiohttp_trust_env: bool = (
|
||||
False # When False, aiohttp will respect HTTP(S)_PROXY env vars
|
||||
)
|
||||
force_ipv4: bool = False # when True, litellm will force ipv4 for all LLM requests. Some users have seen httpx ConnectionError when using ipv6.
|
||||
force_ipv4: bool = (
|
||||
False # when True, litellm will force ipv4 for all LLM requests. Some users have seen httpx ConnectionError when using ipv6.
|
||||
)
|
||||
module_level_aclient = AsyncHTTPHandler(
|
||||
timeout=request_timeout, client_alias="module level aclient"
|
||||
)
|
||||
|
|
@ -410,13 +424,14 @@ fallbacks: Optional[List] = None
|
|||
context_window_fallbacks: Optional[List] = None
|
||||
content_policy_fallbacks: Optional[List] = None
|
||||
allowed_fails: int = 3
|
||||
num_retries_per_request: Optional[
|
||||
int
|
||||
] = None # for the request overall (incl. fallbacks + model retries)
|
||||
allow_dynamic_callback_disabling: bool = True
|
||||
num_retries_per_request: Optional[int] = (
|
||||
None # for the request overall (incl. fallbacks + model retries)
|
||||
)
|
||||
####### SECRET MANAGERS #####################
|
||||
secret_manager_client: Optional[
|
||||
Any
|
||||
] = None # list of instantiated key management clients - e.g. azure kv, infisical, etc.
|
||||
secret_manager_client: Optional[Any] = (
|
||||
None # list of instantiated key management clients - e.g. azure kv, infisical, etc.
|
||||
)
|
||||
_google_kms_resource_name: Optional[str] = None
|
||||
_key_management_system: Optional[KeyManagementSystem] = None
|
||||
_key_management_settings: KeyManagementSettings = KeyManagementSettings()
|
||||
|
|
@ -426,9 +441,9 @@ output_parse_pii: bool = False
|
|||
from litellm.litellm_core_utils.get_model_cost_map import get_model_cost_map
|
||||
|
||||
model_cost = get_model_cost_map(url=model_cost_map_url)
|
||||
cost_discount_config: Dict[
|
||||
str, float
|
||||
] = {} # Provider-specific cost discounts {"vertex_ai": 0.05} = 5% discount
|
||||
cost_discount_config: Dict[str, float] = (
|
||||
{}
|
||||
) # Provider-specific cost discounts {"vertex_ai": 0.05} = 5% discount
|
||||
custom_prompt_dict: Dict[str, dict] = {}
|
||||
check_provider_endpoint = False
|
||||
|
||||
|
|
@ -548,6 +563,7 @@ wandb_models: Set = set(WANDB_MODELS)
|
|||
ovhcloud_models: Set = set()
|
||||
ovhcloud_embedding_models: Set = set()
|
||||
lemonade_models: Set = set()
|
||||
docker_model_runner_models: Set = set()
|
||||
|
||||
|
||||
def is_bedrock_pricing_only_model(key: str) -> bool:
|
||||
|
|
@ -782,6 +798,8 @@ def add_known_models():
|
|||
ovhcloud_embedding_models.add(key)
|
||||
elif value.get("litellm_provider") == "lemonade":
|
||||
lemonade_models.add(key)
|
||||
elif value.get("litellm_provider") == "docker_model_runner":
|
||||
docker_model_runner_models.add(key)
|
||||
|
||||
|
||||
add_known_models()
|
||||
|
|
@ -885,6 +903,7 @@ model_list = list(
|
|||
| wandb_models
|
||||
| ovhcloud_models
|
||||
| lemonade_models
|
||||
| docker_model_runner_models
|
||||
| set(clarifai_models)
|
||||
)
|
||||
|
||||
|
|
@ -1328,10 +1347,14 @@ from .llms.watsonx.completion.transformation import IBMWatsonXAIConfig
|
|||
from .llms.watsonx.chat.transformation import IBMWatsonXChatConfig
|
||||
from .llms.watsonx.embed.transformation import IBMWatsonXEmbeddingConfig
|
||||
from .llms.github_copilot.chat.transformation import GithubCopilotConfig
|
||||
from .llms.github_copilot.responses.transformation import (
|
||||
GithubCopilotResponsesAPIConfig,
|
||||
)
|
||||
from .llms.nebius.chat.transformation import NebiusConfig
|
||||
from .llms.wandb.chat.transformation import WandbConfig
|
||||
from .llms.dashscope.chat.transformation import DashScopeChatConfig
|
||||
from .llms.moonshot.chat.transformation import MoonshotChatConfig
|
||||
from .llms.docker_model_runner.chat.transformation import DockerModelRunnerChatConfig
|
||||
from .llms.v0.chat.transformation import V0ChatConfig
|
||||
from .llms.oci.chat.transformation import OCIChatConfig
|
||||
from .llms.morph.chat.transformation import MorphChatConfig
|
||||
|
|
@ -1342,6 +1365,7 @@ from .llms.ovhcloud.chat.transformation import OVHCloudChatConfig
|
|||
from .llms.ovhcloud.embedding.transformation import OVHCloudEmbeddingConfig
|
||||
from .llms.cometapi.embed.transformation import CometAPIEmbeddingConfig
|
||||
from .llms.lemonade.chat.transformation import LemonadeChatConfig
|
||||
from .llms.snowflake.embedding.transformation import SnowflakeEmbeddingConfig
|
||||
from .main import * # type: ignore
|
||||
from .integrations import *
|
||||
from .llms.custom_httpx.async_client_cleanup import close_litellm_async_clients
|
||||
|
|
@ -1423,12 +1447,12 @@ from .types.llms.custom_llm import CustomLLMItem
|
|||
from .types.utils import GenericStreamingChunk
|
||||
|
||||
custom_provider_map: List[CustomLLMItem] = []
|
||||
_custom_providers: List[
|
||||
str
|
||||
] = [] # internal helper util, used to track names of custom providers
|
||||
disable_hf_tokenizer_download: Optional[
|
||||
bool
|
||||
] = None # disable huggingface tokenizer download. Defaults to openai clk100
|
||||
_custom_providers: List[str] = (
|
||||
[]
|
||||
) # internal helper util, used to track names of custom providers
|
||||
disable_hf_tokenizer_download: Optional[bool] = (
|
||||
None # disable huggingface tokenizer download. Defaults to openai clk100
|
||||
)
|
||||
global_disable_no_log_param: bool = False
|
||||
|
||||
### CLI UTILITIES ###
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from functools import partial
|
|||
from typing import Any, Coroutine, Dict, Literal, Optional, Union, cast
|
||||
|
||||
import httpx
|
||||
from openai.types.batch import BatchRequestCounts
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
|
|
@ -223,10 +224,12 @@ def create_batch(
|
|||
api_key=optional_params.api_key,
|
||||
logging_obj=litellm_logging_obj,
|
||||
_is_async=_is_async,
|
||||
client=client
|
||||
if client is not None
|
||||
and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
|
||||
else None,
|
||||
client=(
|
||||
client
|
||||
if client is not None
|
||||
and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
|
||||
else None
|
||||
),
|
||||
timeout=timeout,
|
||||
model=model,
|
||||
)
|
||||
|
|
@ -609,10 +612,12 @@ def retrieve_batch(
|
|||
function_id="batch_retrieve",
|
||||
),
|
||||
_is_async=_is_async,
|
||||
client=client
|
||||
if client is not None
|
||||
and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
|
||||
else None,
|
||||
client=(
|
||||
client
|
||||
if client is not None
|
||||
and isinstance(client, (HTTPHandler, AsyncHTTPHandler))
|
||||
else None
|
||||
),
|
||||
timeout=timeout,
|
||||
model=model,
|
||||
)
|
||||
|
|
@ -799,6 +804,7 @@ def list_batches(
|
|||
|
||||
async def acancel_batch(
|
||||
batch_id: str,
|
||||
model: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
metadata: Optional[Dict[str, str]] = None,
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
|
|
@ -813,11 +819,13 @@ async def acancel_batch(
|
|||
try:
|
||||
loop = asyncio.get_event_loop()
|
||||
kwargs["acancel_batch"] = True
|
||||
model = kwargs.pop("model", None)
|
||||
|
||||
# Use a partial function to pass your keyword arguments
|
||||
func = partial(
|
||||
cancel_batch,
|
||||
batch_id,
|
||||
model,
|
||||
custom_llm_provider,
|
||||
metadata,
|
||||
extra_headers,
|
||||
|
|
@ -840,7 +848,8 @@ async def acancel_batch(
|
|||
|
||||
def cancel_batch(
|
||||
batch_id: str,
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
model: Optional[str] = None,
|
||||
custom_llm_provider: Union[Literal["openai", "azure"], str] = "openai",
|
||||
metadata: Optional[Dict[str, str]] = None,
|
||||
extra_headers: Optional[Dict[str, str]] = None,
|
||||
extra_body: Optional[Dict[str, str]] = None,
|
||||
|
|
@ -852,6 +861,17 @@ def cancel_batch(
|
|||
LiteLLM Equivalent of POST https://api.openai.com/v1/batches/{batch_id}/cancel
|
||||
"""
|
||||
try:
|
||||
|
||||
try:
|
||||
if model is not None:
|
||||
_, custom_llm_provider, _, _ = get_llm_provider(
|
||||
model=model,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
)
|
||||
except Exception as e:
|
||||
verbose_logger.exception(
|
||||
f"litellm.batches.main.py::cancel_batch() - Error inferring custom_llm_provider - {str(e)}"
|
||||
)
|
||||
optional_params = GenericLiteLLMParams(**kwargs)
|
||||
litellm_params = get_litellm_params(
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
|
|
@ -1005,21 +1025,28 @@ def _handle_async_invoke_status(
|
|||
created_at=status_response["submitTime"],
|
||||
in_progress_at=status_response["lastModifiedTime"],
|
||||
completed_at=status_response.get("endTime"),
|
||||
failed_at=status_response.get("endTime")
|
||||
if status_response["status"] == "failed"
|
||||
else None,
|
||||
request_counts={
|
||||
"total": 1,
|
||||
"completed": 1 if status_response["status"] == "completed" else 0,
|
||||
"failed": 1 if status_response["status"] == "failed" else 0,
|
||||
},
|
||||
metadata={
|
||||
"output_file_id": status_response["outputDataConfig"][
|
||||
"s3OutputDataConfig"
|
||||
]["s3Uri"],
|
||||
"failure_message": status_response.get("failureMessage"),
|
||||
"model_arn": status_response["modelArn"],
|
||||
},
|
||||
failed_at=(
|
||||
status_response.get("endTime")
|
||||
if status_response["status"] == "failed"
|
||||
else None
|
||||
),
|
||||
request_counts=BatchRequestCounts(
|
||||
total=1,
|
||||
completed=1 if status_response["status"] == "completed" else 0,
|
||||
failed=1 if status_response["status"] == "failed" else 0,
|
||||
),
|
||||
metadata=dict(
|
||||
**{
|
||||
"output_file_id": status_response["outputDataConfig"][
|
||||
"s3OutputDataConfig"
|
||||
]["s3Uri"],
|
||||
"failure_message": status_response.get("failureMessage") or "",
|
||||
"model_arn": status_response["modelArn"],
|
||||
}
|
||||
),
|
||||
completion_window="24h",
|
||||
endpoint="/v1/embeddings",
|
||||
input_file_id="",
|
||||
)
|
||||
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class RedisCache(BaseCache):
|
|||
connection_pool=self.async_redis_conn_pool, **self.redis_kwargs
|
||||
)
|
||||
in_memory_llm_clients_cache.set_cache(
|
||||
key="async-redis-client", value=self.redis_async_client
|
||||
key="async-redis-client", value=redis_async_client
|
||||
)
|
||||
|
||||
self.redis_async_client = redis_async_client # type: ignore
|
||||
|
|
|
|||