mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
Merge branch 'BerriAI:main' into fix/vertex-gemini-multimodal-drop
This commit is contained in:
commit
f6b458ec2f
121 changed files with 2777 additions and 1356 deletions
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git show:*)",
|
||||
"Bash(git worktree add:*)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm/**)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm-claude-code-guardrails/litellm/types/**)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm-claude-code-guardrails/**)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm-claude-code-guardrails/litellm/**)",
|
||||
"Bash(python:*)",
|
||||
"Bash(python -c \"\nimport sys; sys.path.insert\\(0, ''.''\\)\nfrom litellm.proxy.guardrails.guardrail_hooks.claude_code.guardrail import ClaudeCodeGuardrail, HOSTED_TOOL_PREFIXES\nprint\\(''HOSTED_TOOL_PREFIXES:'', HOSTED_TOOL_PREFIXES\\)\nprint\\(''ClaudeCodeGuardrail imported OK''\\)\n\")",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm-mcp-jwt-groups/litellm/proxy/**)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm-mcp-jwt-groups/**)",
|
||||
"Bash(poetry run pytest:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(poetry run python:*)",
|
||||
"Bash(poetry run pip:*)",
|
||||
"Bash(git reset:*)",
|
||||
"Bash(git cherry-pick:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Read(//Users/krrishdholakia/Documents/litellm/litellm/proxy/guardrails/guardrail_hooks/**)",
|
||||
"Read(//Users/krrishdholakia/Documents/**)",
|
||||
"Bash(git -C /Users/krrishdholakia/Documents/litellm-mcp-user-permissions worktree list)",
|
||||
"Bash(ls:*)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"/Users/krrishdholakia/Documents/litellm-mcp-group-plan/plan",
|
||||
"/Users/krrishdholakia/Documents/litellm-claude-code-guardrails/litellm/proxy/guardrails/guardrail_hooks/claude_code",
|
||||
"/Users/krrishdholakia/Documents/litellm-claude-code-guardrails/litellm/types",
|
||||
"/Users/krrishdholakia/Documents/litellm-claude-code-guardrails",
|
||||
"/Users/krrishdholakia/Documents/litellm-mcp-jwt-groups/litellm/proxy",
|
||||
"/Users/krrishdholakia/Documents/litellm-mcp-jwt-groups/tests/test_litellm/proxy/auth"
|
||||
]
|
||||
}
|
||||
}
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Schedule Demo
|
||||
url: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions
|
||||
url: https://enterprise.litellm.ai/demo
|
||||
about: Speak directly with Krrish and Ishaan, the founders, to discuss issues, share feedback, or explore improvements for LiteLLM
|
||||
- name: Discord
|
||||
url: https://discord.com/invite/wuPM9dRgDw
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ name: Updates model_prices_and_context_window.json and Create Pull Request
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * 0" # Run every Sundays at midnight
|
||||
- cron: "0 0 * * 0" # Run every Sundays at midnight
|
||||
#- cron: "0 0 * * *" # Run daily at midnight
|
||||
|
||||
jobs:
|
||||
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install aiohttp
|
||||
|
|
@ -26,4 +26,4 @@ jobs:
|
|||
--head auto-update-price-and-context-window-$(date +'%Y-%m-%d') \
|
||||
--base main
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
|
|
|
|||
6
.github/workflows/check_duplicate_issues.yml
vendored
6
.github/workflows/check_duplicate_issues.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
contents: read
|
||||
steps:
|
||||
- name: Check for potential duplicates
|
||||
uses: wow-actions/potential-duplicates@v1
|
||||
uses: wow-actions/potential-duplicates@4d4ea0352e0383859279938e255179dd1dbb67b5 # v1.1.0
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
label: potential-duplicate
|
||||
|
|
@ -30,13 +30,13 @@ jobs:
|
|||
|
||||
- name: Checkout close script
|
||||
if: github.event.action == 'opened'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
with:
|
||||
sparse-checkout: .github/scripts
|
||||
|
||||
- name: Set up Python
|
||||
if: github.event.action == 'opened'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
|
|
|
|||
7
.github/workflows/codeql.yml
vendored
7
.github/workflows/codeql.yml
vendored
|
|
@ -15,6 +15,7 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
analyze:
|
||||
if: github.event_name != 'schedule' || github.repository == 'BerriAI/litellm'
|
||||
name: Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
|
@ -37,16 +38,16 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@ebcb5b36ded6beda4ceefea6a8bc4cc885255bb3 # v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
|
|
|||
6
.github/workflows/codspeed.yml
vendored
6
.github/workflows/codspeed.yml
vendored
|
|
@ -25,10 +25,10 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
pip install pytest pytest-codspeed==4.3.0
|
||||
|
||||
- name: Run benchmarks
|
||||
uses: CodSpeedHQ/action@v4
|
||||
uses: CodSpeedHQ/action@1c8ae4843586d3ba879736b7f6b7b0c990757fab # v4.12.1
|
||||
with:
|
||||
mode: simulation
|
||||
run: pytest tests/benchmarks/ --codspeed
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@ name: Create Daily Staging Branch
|
|||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0,12 * * *' # Runs every 12 hours at midnight and noon UTC
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
- cron: "0 0,12 * * *" # Runs every 12 hours at midnight and noon UTC
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
jobs:
|
||||
create-staging-branch:
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
@ -43,11 +44,12 @@ jobs:
|
|||
fi
|
||||
|
||||
create-internal-dev-branch:
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
|
|||
444
.github/workflows/ghcr_deploy.yml
vendored
444
.github/workflows/ghcr_deploy.yml
vendored
|
|
@ -1,444 +0,0 @@
|
|||
# this workflow is triggered by an API call when there is a new PyPI release of LiteLLM
|
||||
name: Build, Publish LiteLLM Docker Image. New Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The tag version you want to build"
|
||||
required: true
|
||||
release_type:
|
||||
description: "The release type you want to build. Can be 'latest', 'stable', 'dev', 'rc'"
|
||||
type: string
|
||||
default: "latest"
|
||||
commit_hash:
|
||||
description: "Commit hash"
|
||||
required: true
|
||||
|
||||
# Defines two custom environment variables for the workflow. Used for the Container registry domain, and a name for the Docker image that this workflow builds.
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
CHART_NAME: litellm-helm
|
||||
|
||||
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
|
||||
jobs:
|
||||
# print commit hash, tag, and release type
|
||||
print:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: |
|
||||
echo "Commit hash: ${{ github.event.inputs.commit_hash }}"
|
||||
echo "Tag: ${{ github.event.inputs.tag }}"
|
||||
echo "Release type: ${{ github.event.inputs.release_type }}"
|
||||
docker-hub-deploy:
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: litellm/litellm:${{ github.event.inputs.tag || 'latest' }}
|
||||
-
|
||||
name: Build and push litellm-database image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: ./docker/Dockerfile.database
|
||||
tags: litellm/litellm-database:${{ github.event.inputs.tag || 'latest' }}
|
||||
-
|
||||
name: Build and push litellm-spend-logs image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: ./litellm-js/spend-logs/Dockerfile
|
||||
tags: litellm/litellm-spend_logs:${{ github.event.inputs.tag || 'latest' }}
|
||||
-
|
||||
name: Build and push litellm-non_root image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: ./docker/Dockerfile.non_root
|
||||
tags: litellm/litellm-non_root:${{ github.event.inputs.tag || 'latest' }}
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
# Configure multi platform Docker builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
|
||||
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
|
||||
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
|
||||
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@4976231911ebf5f32aad765192d35f942aa48cb8
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
|
||||
${{ steps.meta.outputs.tags }}-${{ github.event.inputs.release_type }}
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm:main-stable', env.REGISTRY) || '' }},
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm:{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
|
||||
|
||||
build-and-push-image-ee:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for EE Dockerfile
|
||||
id: meta-ee
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee
|
||||
# Configure multi platform Docker builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
|
||||
|
||||
- name: Build and push EE Docker image
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta-ee.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
|
||||
${{ steps.meta-ee.outputs.tags }}-${{ github.event.inputs.release_type }}
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm-ee:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-ee:main-stable', env.REGISTRY) || '' }}
|
||||
labels: ${{ steps.meta-ee.outputs.labels }}
|
||||
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
|
||||
|
||||
build-and-push-image-database:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for database Dockerfile
|
||||
id: meta-database
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-database
|
||||
# Configure multi platform Docker builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
|
||||
|
||||
- name: Build and push Database Docker image
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile.database
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
|
||||
${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.release_type }}
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm-database:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-database:main-stable', env.REGISTRY) || '' }}
|
||||
labels: ${{ steps.meta-database.outputs.labels }}
|
||||
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
|
||||
|
||||
build-and-push-image-non_root:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for non_root Dockerfile
|
||||
id: meta-non_root
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-non_root
|
||||
# Configure multi platform Docker builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
|
||||
|
||||
- name: Build and push non_root Docker image
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile.non_root
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta-non_root.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
|
||||
${{ steps.meta-non_root.outputs.tags }}-${{ github.event.inputs.release_type }}
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm-non_root:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-non_root:main-stable', env.REGISTRY) || '' }}
|
||||
labels: ${{ steps.meta-non_root.outputs.labels }}
|
||||
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
|
||||
|
||||
build-and-push-image-spend-logs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.commit_hash }}
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for spend-logs Dockerfile
|
||||
id: meta-spend-logs
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-spend_logs
|
||||
# Configure multi platform Docker builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345
|
||||
|
||||
- name: Build and push Database Docker image
|
||||
uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4
|
||||
with:
|
||||
context: .
|
||||
file: ./litellm-js/spend-logs/Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.meta-spend-logs.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }},
|
||||
${{ steps.meta-spend-logs.outputs.tags }}-${{ github.event.inputs.release_type }}
|
||||
${{ (github.event.inputs.release_type == 'stable' || github.event.inputs.release_type == 'rc') && format('{0}/berriai/litellm-spend_logs:main-{1}', env.REGISTRY, github.event.inputs.tag) || '' }},
|
||||
${{ github.event.inputs.release_type == 'stable' && format('{0}/berriai/litellm-spend_logs:main-stable', env.REGISTRY) || '' }}
|
||||
platforms: local,linux/amd64,linux/arm64,linux/arm64/v8
|
||||
|
||||
run-observatory-tests:
|
||||
if: github.event.inputs.release_type == 'rc' || github.event.inputs.release_type == 'stable'
|
||||
needs: [docker-hub-deploy]
|
||||
uses: ./.github/workflows/run_observatory_tests.yml
|
||||
with:
|
||||
tag: ${{ github.event.inputs.tag }}
|
||||
commit_hash: ${{ github.event.inputs.commit_hash }}
|
||||
secrets: inherit
|
||||
|
||||
build-and-push-helm-chart:
|
||||
if: github.event.inputs.release_type != 'dev'
|
||||
needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: lowercase github.repository_owner
|
||||
run: |
|
||||
echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV}
|
||||
|
||||
# Sync Helm chart version with LiteLLM release version (1-1 versioning)
|
||||
# This allows users to easily map Helm chart versions to LiteLLM versions
|
||||
# See: https://codefresh.io/docs/docs/ci-cd-guides/helm-best-practices/
|
||||
- name: Calculate chart and app versions
|
||||
id: chart_version
|
||||
shell: bash
|
||||
run: |
|
||||
INPUT_TAG="${{ github.event.inputs.tag }}"
|
||||
RELEASE_TYPE="${{ github.event.inputs.release_type }}"
|
||||
|
||||
# Chart version = LiteLLM version without 'v' prefix (Helm semver convention)
|
||||
# v1.81.0 -> 1.81.0, v1.81.0.rc.1 -> 1.81.0.rc.1
|
||||
CHART_VERSION="${INPUT_TAG#v}"
|
||||
|
||||
# Add suffix for 'latest' releases (rc already has suffix in tag)
|
||||
if [ "$RELEASE_TYPE" = "latest" ]; then
|
||||
CHART_VERSION="${CHART_VERSION}-latest"
|
||||
fi
|
||||
|
||||
# App version = Docker tag (keeps 'v' prefix to match Docker image tags)
|
||||
APP_VERSION="${INPUT_TAG}"
|
||||
|
||||
echo "version=${CHART_VERSION}" | tee -a $GITHUB_OUTPUT
|
||||
echo "app_version=${APP_VERSION}" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- uses: ./.github/actions/helm-oci-chart-releaser
|
||||
with:
|
||||
name: ${{ env.CHART_NAME }}
|
||||
repository: ${{ env.REPO_OWNER }}
|
||||
tag: ${{ steps.chart_version.outputs.version }}
|
||||
app_version: ${{ steps.chart_version.outputs.app_version }}
|
||||
path: deploy/charts/${{ env.CHART_NAME }}
|
||||
registry: ${{ env.REGISTRY }}
|
||||
registry_username: ${{ github.actor }}
|
||||
registry_password: ${{ secrets.GITHUB_TOKEN }}
|
||||
update_dependencies: true
|
||||
|
||||
release:
|
||||
name: "New LiteLLM Release"
|
||||
needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database]
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- name: Display version
|
||||
run: echo "Current version is ${{ github.event.inputs.tag }}"
|
||||
- name: "Set Release Tag"
|
||||
run: echo "RELEASE_TAG=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
|
||||
- name: Display release tag
|
||||
run: echo "RELEASE_TAG is $RELEASE_TAG"
|
||||
- name: "Create release"
|
||||
uses: "actions/github-script@v6"
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
script: |
|
||||
const commitHash = "${{ github.event.inputs.commit_hash}}";
|
||||
console.log("Commit Hash:", commitHash); // Add this line for debugging
|
||||
try {
|
||||
const response = await github.rest.repos.createRelease({
|
||||
draft: false,
|
||||
generate_release_notes: true,
|
||||
target_commitish: commitHash,
|
||||
name: process.env.RELEASE_TAG,
|
||||
owner: context.repo.owner,
|
||||
prerelease: false,
|
||||
repo: context.repo.repo,
|
||||
tag_name: process.env.RELEASE_TAG,
|
||||
});
|
||||
|
||||
core.exportVariable('RELEASE_ID', response.data.id);
|
||||
core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url);
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
- name: Fetch Release Notes
|
||||
id: release-notes
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
script: |
|
||||
try {
|
||||
const response = await github.rest.repos.getRelease({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
release_id: process.env.RELEASE_ID,
|
||||
});
|
||||
const formattedBody = JSON.stringify(response.data.body).slice(1, -1);
|
||||
return formattedBody;
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
env:
|
||||
RELEASE_ID: ${{ env.RELEASE_ID }}
|
||||
- name: Github Releases To Discord
|
||||
env:
|
||||
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
|
||||
REALEASE_TAG: ${{ env.RELEASE_TAG }}
|
||||
RELEASE_NOTES: ${{ steps.release-notes.outputs.result }}
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" -X POST -d '{
|
||||
"content": "New LiteLLM release '"${RELEASE_TAG}"'",
|
||||
"username": "Release Changelog",
|
||||
"avatar_url": "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png",
|
||||
"embeds": [
|
||||
{
|
||||
"title": "Changelog for LiteLLM '"${RELEASE_TAG}"'",
|
||||
"description": "'"${RELEASE_NOTES}"'",
|
||||
"color": 2105893
|
||||
}
|
||||
]
|
||||
}' $WEBHOOK_URL
|
||||
|
||||
67
.github/workflows/ghcr_helm_deploy.yml
vendored
67
.github/workflows/ghcr_helm_deploy.yml
vendored
|
|
@ -1,67 +0,0 @@
|
|||
# Standalone workflow to publish LiteLLM Helm Chart
|
||||
# Note: The main ghcr_deploy.yml workflow also publishes the Helm chart as part of a full release
|
||||
name: Build, Publish LiteLLM Helm Chart. New Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "LiteLLM version tag (e.g., v1.81.0)"
|
||||
required: true
|
||||
|
||||
# Defines two custom environment variables for the workflow. Used for the Container registry domain, and a name for the Docker image that this workflow builds.
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
REPO_OWNER: ${{github.repository_owner}}
|
||||
|
||||
# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu.
|
||||
jobs:
|
||||
build-and-push-helm-chart:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: lowercase github.repository_owner
|
||||
run: |
|
||||
echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV}
|
||||
|
||||
# Sync Helm chart version with LiteLLM release version (1-1 versioning)
|
||||
- name: Calculate chart and app versions
|
||||
id: chart_version
|
||||
shell: bash
|
||||
run: |
|
||||
INPUT_TAG="${{ github.event.inputs.tag }}"
|
||||
|
||||
# Chart version = LiteLLM version without 'v' prefix
|
||||
# v1.81.0 -> 1.81.0
|
||||
CHART_VERSION="${INPUT_TAG#v}"
|
||||
|
||||
# App version = Docker tag (keeps 'v' prefix)
|
||||
APP_VERSION="${INPUT_TAG}"
|
||||
|
||||
echo "version=${CHART_VERSION}" | tee -a $GITHUB_OUTPUT
|
||||
echo "app_version=${APP_VERSION}" | tee -a $GITHUB_OUTPUT
|
||||
|
||||
- name: Lint helm chart
|
||||
run: helm lint deploy/charts/litellm-helm
|
||||
|
||||
- uses: ./.github/actions/helm-oci-chart-releaser
|
||||
with:
|
||||
name: litellm-helm
|
||||
repository: ${{ env.REPO_OWNER }}
|
||||
tag: ${{ steps.chart_version.outputs.version }}
|
||||
app_version: ${{ steps.chart_version.outputs.app_version }}
|
||||
path: deploy/charts/litellm-helm
|
||||
registry: ${{ env.REGISTRY }}
|
||||
registry_username: ${{ github.actor }}
|
||||
registry_password: ${{ secrets.GITHUB_TOKEN }}
|
||||
update_dependencies: true
|
||||
|
||||
19
.github/workflows/helm_unit_test.yml
vendored
19
.github/workflows/helm_unit_test.yml
vendored
|
|
@ -11,17 +11,26 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Set up Helm 3.11.1
|
||||
uses: azure/setup-helm@v1
|
||||
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4.3.1
|
||||
with:
|
||||
version: '3.11.1'
|
||||
version: "3.11.1"
|
||||
|
||||
- name: Install Helm Unit Test Plugin
|
||||
run: |
|
||||
helm plugin install https://github.com/helm-unittest/helm-unittest --version v0.4.4
|
||||
- name: Verify Helm Unit Test Plugin integrity
|
||||
run: |
|
||||
EXPECTED_SHA="e251ba198448629678ff2168e1a469249d998155"
|
||||
PLUGIN_DIR="$(helm env HELM_PLUGINS)/helm-unittest"
|
||||
ACTUAL_SHA="$(git -C "$PLUGIN_DIR" rev-parse HEAD)"
|
||||
if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then
|
||||
echo "::error::Helm unittest plugin checksum mismatch! Expected $EXPECTED_SHA but got $ACTUAL_SHA"
|
||||
exit 1
|
||||
fi
|
||||
echo "Helm unittest plugin integrity verified: $ACTUAL_SHA"
|
||||
|
||||
- name: Run unit tests
|
||||
run:
|
||||
helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm
|
||||
run: helm unittest -f 'tests/*.yaml' deploy/charts/litellm-helm
|
||||
|
|
|
|||
2
.github/workflows/interpret_load_test.py
vendored
2
.github/workflows/interpret_load_test.py
vendored
|
|
@ -123,7 +123,7 @@ if __name__ == "__main__":
|
|||
+ docker_run_command
|
||||
+ "\n\n"
|
||||
+ "### Don't want to maintain your internal proxy? get in touch 🎉"
|
||||
+ "\nHosted Proxy Alpha: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions"
|
||||
+ "\nHosted Proxy Alpha: https://enterprise.litellm.ai/demo"
|
||||
+ "\n\n"
|
||||
+ "## Load Test LiteLLM Proxy Results"
|
||||
+ "\n\n"
|
||||
|
|
|
|||
11
.github/workflows/issue-keyword-labeler.yml
vendored
11
.github/workflows/issue-keyword-labeler.yml
vendored
|
|
@ -2,8 +2,8 @@ name: Issue Keyword Labeler
|
|||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
scan-and-label:
|
||||
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
contents: read
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
|
||||
- name: Scan for provider keywords
|
||||
id: scan
|
||||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
|
||||
- name: Ensure label exists
|
||||
if: steps.scan.outputs.found == 'true'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
|
@ -51,7 +51,7 @@ jobs:
|
|||
|
||||
- name: Add label to the issue
|
||||
if: steps.scan.outputs.found == 'true'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
|
@ -61,4 +61,3 @@ jobs:
|
|||
issue_number: context.issue.number,
|
||||
labels: ['llm translation']
|
||||
});
|
||||
|
||||
|
|
|
|||
2
.github/workflows/label-component.yml
vendored
2
.github/workflows/label-component.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
issues: write
|
||||
steps:
|
||||
- name: Add component labels
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
34
.github/workflows/main.yml
vendored
34
.github/workflows/main.yml
vendored
|
|
@ -1,34 +0,0 @@
|
|||
name: Publish Dev Release to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-dev-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8 # Adjust the Python version as needed
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install toml twine
|
||||
|
||||
- name: Read version from pyproject.toml
|
||||
id: read-version
|
||||
run: |
|
||||
version=$(python -c 'import toml; print(toml.load("pyproject.toml")["tool"]["commitizen"]["version"])')
|
||||
printf "LITELLM_VERSION=%s" "$version" >> $GITHUB_ENV
|
||||
|
||||
- name: Check if version exists on PyPI
|
||||
id: check-version
|
||||
run: |
|
||||
set -e
|
||||
if twine check --repository-url https://pypi.org/simple/ "litellm==$LITELLM_VERSION" >/dev/null 2>&1; then
|
||||
echo "Version $LITELLM_VERSION already exists on PyPI. Skipping publish."
|
||||
|
||||
207
.github/workflows/publish-migrations.yml
vendored
207
.github/workflows/publish-migrations.yml
vendored
|
|
@ -1,207 +0,0 @@
|
|||
name: Publish Prisma Migrations
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'schema.prisma' # Check root schema.prisma
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
publish-migrations:
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_DB: temp_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
# Add shadow database service
|
||||
postgres_shadow:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_DB: shadow_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- 5433:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
pip install prisma
|
||||
pip install python-dotenv
|
||||
|
||||
- name: Generate Initial Migration if None Exists
|
||||
env:
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
DIRECT_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
SHADOW_DATABASE_URL: "postgresql://postgres:postgres@localhost:5433/shadow_db"
|
||||
run: |
|
||||
mkdir -p deploy/migrations
|
||||
echo 'provider = "postgresql"' > deploy/migrations/migration_lock.toml
|
||||
|
||||
if [ -z "$(ls -A deploy/migrations/2* 2>/dev/null)" ]; then
|
||||
echo "No existing migrations found, creating baseline..."
|
||||
VERSION=$(date +%Y%m%d%H%M%S)
|
||||
mkdir -p deploy/migrations/${VERSION}_initial
|
||||
|
||||
echo "Generating initial migration..."
|
||||
# Save raw output for debugging
|
||||
prisma migrate diff \
|
||||
--from-empty \
|
||||
--to-schema-datamodel schema.prisma \
|
||||
--shadow-database-url "${SHADOW_DATABASE_URL}" \
|
||||
--script > deploy/migrations/${VERSION}_initial/raw_migration.sql
|
||||
|
||||
echo "Raw migration file content:"
|
||||
cat deploy/migrations/${VERSION}_initial/raw_migration.sql
|
||||
|
||||
echo "Cleaning migration file..."
|
||||
# Clean the file
|
||||
sed '/^Installing/d' deploy/migrations/${VERSION}_initial/raw_migration.sql > deploy/migrations/${VERSION}_initial/migration.sql
|
||||
|
||||
# Verify the migration file
|
||||
if [ ! -s deploy/migrations/${VERSION}_initial/migration.sql ]; then
|
||||
echo "ERROR: Migration file is empty after cleaning"
|
||||
echo "Original content was:"
|
||||
cat deploy/migrations/${VERSION}_initial/raw_migration.sql
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Final migration file content:"
|
||||
cat deploy/migrations/${VERSION}_initial/migration.sql
|
||||
|
||||
# Verify it starts with SQL
|
||||
if ! head -n 1 deploy/migrations/${VERSION}_initial/migration.sql | grep -q "^--\|^CREATE\|^ALTER"; then
|
||||
echo "ERROR: Migration file does not start with SQL command or comment"
|
||||
echo "First line is:"
|
||||
head -n 1 deploy/migrations/${VERSION}_initial/migration.sql
|
||||
echo "Full content is:"
|
||||
cat deploy/migrations/${VERSION}_initial/migration.sql
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Initial migration generated at $(date -u)" > deploy/migrations/${VERSION}_initial/README.md
|
||||
fi
|
||||
|
||||
- name: Compare and Generate Migration
|
||||
if: success()
|
||||
env:
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
DIRECT_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
SHADOW_DATABASE_URL: "postgresql://postgres:postgres@localhost:5433/shadow_db"
|
||||
run: |
|
||||
# Create temporary migration workspace
|
||||
mkdir -p temp_migrations
|
||||
|
||||
# Copy existing migrations (will not fail if directory is empty)
|
||||
cp -r deploy/migrations/* temp_migrations/ 2>/dev/null || true
|
||||
|
||||
VERSION=$(date +%Y%m%d%H%M%S)
|
||||
|
||||
# Generate diff against existing migrations or empty state
|
||||
prisma migrate diff \
|
||||
--from-migrations temp_migrations \
|
||||
--to-schema-datamodel schema.prisma \
|
||||
--shadow-database-url "${SHADOW_DATABASE_URL}" \
|
||||
--script > temp_migrations/migration_${VERSION}.sql
|
||||
|
||||
# Check if there are actual changes
|
||||
if [ -s temp_migrations/migration_${VERSION}.sql ]; then
|
||||
echo "Changes detected, creating new migration"
|
||||
mkdir -p deploy/migrations/${VERSION}_schema_update
|
||||
mv temp_migrations/migration_${VERSION}.sql deploy/migrations/${VERSION}_schema_update/migration.sql
|
||||
echo "Migration generated at $(date -u)" > deploy/migrations/${VERSION}_schema_update/README.md
|
||||
else
|
||||
echo "No schema changes detected"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
- name: Verify Migration
|
||||
if: success()
|
||||
env:
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
DIRECT_URL: "postgresql://postgres:postgres@localhost:5432/temp_db"
|
||||
SHADOW_DATABASE_URL: "postgresql://postgres:postgres@localhost:5433/shadow_db"
|
||||
run: |
|
||||
# Create test database
|
||||
psql "${SHADOW_DATABASE_URL}" -c 'CREATE DATABASE migration_test;'
|
||||
|
||||
# Apply all migrations in order to verify
|
||||
for migration in deploy/migrations/*/migration.sql; do
|
||||
echo "Applying migration: $migration"
|
||||
psql "${SHADOW_DATABASE_URL}" -f $migration
|
||||
done
|
||||
|
||||
# Add this step before create-pull-request to debug permissions
|
||||
- name: Check Token Permissions
|
||||
run: |
|
||||
echo "Checking token permissions..."
|
||||
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/BerriAI/litellm/collaborators
|
||||
|
||||
echo "\nChecking if token can create PRs..."
|
||||
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/BerriAI/litellm
|
||||
|
||||
# Add this debug step before git push
|
||||
- name: Debug Changed Files
|
||||
run: |
|
||||
echo "Files staged for commit:"
|
||||
git diff --name-status --staged
|
||||
|
||||
echo "\nAll changed files:"
|
||||
git status
|
||||
|
||||
- name: Create Pull Request
|
||||
if: success()
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: "chore: update prisma migrations"
|
||||
title: "Update Prisma Migrations"
|
||||
body: |
|
||||
Auto-generated migration based on schema.prisma changes.
|
||||
|
||||
Generated files:
|
||||
- deploy/migrations/${VERSION}_schema_update/migration.sql
|
||||
- deploy/migrations/${VERSION}_schema_update/README.md
|
||||
branch: feat/prisma-migration-${{ env.VERSION }}
|
||||
base: main
|
||||
delete-branch: true
|
||||
|
||||
- name: Generate and Save Migrations
|
||||
run: |
|
||||
# Only add migration files
|
||||
git add deploy/migrations/
|
||||
git status # Debug what's being committed
|
||||
git commit -m "chore: update prisma migrations"
|
||||
94
.github/workflows/publish_enterprise.yml
vendored
94
.github/workflows/publish_enterprise.yml
vendored
|
|
@ -1,94 +0,0 @@
|
|||
name: Publish litellm-enterprise to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump:
|
||||
description: "Version bump type"
|
||||
required: true
|
||||
default: "patch"
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
defaults:
|
||||
run:
|
||||
working-directory: enterprise
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install Poetry
|
||||
run: pip install poetry
|
||||
|
||||
- name: Bump version
|
||||
id: bump
|
||||
run: |
|
||||
OLD=$(poetry version -s)
|
||||
poetry version ${{ github.event.inputs.bump }}
|
||||
NEW=$(poetry version -s)
|
||||
echo "old=$OLD" >> $GITHUB_OUTPUT
|
||||
echo "new=$NEW" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version refs in root pyproject.toml and requirements.txt
|
||||
run: |
|
||||
OLD=${{ steps.bump.outputs.old }}
|
||||
NEW=${{ steps.bump.outputs.new }}
|
||||
sed -i "s/litellm-enterprise = {version = \"${OLD}\"/litellm-enterprise = {version = \"${NEW}\"/" ../pyproject.toml
|
||||
sed -i "s/litellm-enterprise==${OLD}/litellm-enterprise==${NEW}/" ../requirements.txt
|
||||
|
||||
- name: Update poetry.lock
|
||||
working-directory: .
|
||||
run: poetry lock
|
||||
|
||||
- name: Build
|
||||
run: poetry build
|
||||
|
||||
- name: Commit version bump and create PR
|
||||
id: create-pr
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
cd ..
|
||||
BRANCH="bump/enterprise-${{ steps.bump.outputs.new }}"
|
||||
git checkout -b "$BRANCH"
|
||||
git add enterprise/pyproject.toml pyproject.toml requirements.txt poetry.lock
|
||||
git commit -m "bump: litellm-enterprise ${{ steps.bump.outputs.old }} → ${{ steps.bump.outputs.new }}"
|
||||
git push origin "$BRANCH" --force
|
||||
gh pr create \
|
||||
--title "bump: litellm-enterprise ${{ steps.bump.outputs.old }} → ${{ steps.bump.outputs.new }}" \
|
||||
--body "Version bump for litellm-enterprise. Merge to update main." \
|
||||
--head "$BRANCH" \
|
||||
--base main \
|
||||
|| true
|
||||
PR_URL=$(gh pr list --head "$BRANCH" --json url -q '.[0].url')
|
||||
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Enable auto-merge
|
||||
run: |
|
||||
gh pr merge "${{ steps.create-pr.outputs.pr_url }}" --auto --squash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_ENTERPRISE }}
|
||||
run: |
|
||||
pip install twine
|
||||
twine upload dist/litellm_enterprise-${{ steps.bump.outputs.new }}*
|
||||
74
.github/workflows/publish_proxy_extras.yml
vendored
74
.github/workflows/publish_proxy_extras.yml
vendored
|
|
@ -1,74 +0,0 @@
|
|||
name: Publish litellm-proxy-extras to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump:
|
||||
description: "Version bump type"
|
||||
required: true
|
||||
default: "patch"
|
||||
type: choice
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
permissions:
|
||||
contents: write
|
||||
defaults:
|
||||
run:
|
||||
working-directory: litellm-proxy-extras
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install Poetry
|
||||
run: pip install poetry
|
||||
|
||||
- name: Bump version
|
||||
id: bump
|
||||
run: |
|
||||
OLD=$(poetry version -s)
|
||||
poetry version ${{ github.event.inputs.bump }}
|
||||
NEW=$(poetry version -s)
|
||||
echo "old=$OLD" >> $GITHUB_OUTPUT
|
||||
echo "new=$NEW" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version refs in root pyproject.toml and requirements.txt
|
||||
run: |
|
||||
OLD=${{ steps.bump.outputs.old }}
|
||||
NEW=${{ steps.bump.outputs.new }}
|
||||
sed -i "s/litellm-proxy-extras = {version = \"${OLD}\"/litellm-proxy-extras = {version = \"${NEW}\"/" ../pyproject.toml
|
||||
sed -i "s/litellm-proxy-extras==${OLD}/litellm-proxy-extras==${NEW}/" ../requirements.txt
|
||||
|
||||
- name: Update poetry.lock
|
||||
working-directory: .
|
||||
run: poetry lock
|
||||
|
||||
- name: Build
|
||||
run: poetry build
|
||||
|
||||
- name: Commit version bump
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
cd ..
|
||||
git add litellm-proxy-extras/pyproject.toml pyproject.toml requirements.txt poetry.lock
|
||||
git commit -m "bump: litellm-proxy-extras ${{ steps.bump.outputs.old }} → ${{ steps.bump.outputs.new }}"
|
||||
git push
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PUBLISH_PASSWORD }}
|
||||
run: |
|
||||
pip install twine
|
||||
twine upload dist/litellm_proxy_extras-${{ steps.bump.outputs.new }}*
|
||||
39
.github/workflows/reset_stable.yml
vendored
39
.github/workflows/reset_stable.yml
vendored
|
|
@ -1,39 +0,0 @@
|
|||
name: Reset litellm_stable branch
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, created]
|
||||
jobs:
|
||||
update-stable-branch:
|
||||
if: ${{ startsWith(github.event.release.tag_name, 'v') && !endsWith(github.event.release.tag_name, '-stable') }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Reset litellm_stable_release_branch branch to the release commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Configure Git user
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Fetch all branches and tags
|
||||
git fetch --all
|
||||
|
||||
# Check if the litellm_stable_release_branch branch exists
|
||||
if git show-ref --verify --quiet refs/remotes/origin/litellm_stable_release_branch; then
|
||||
echo "litellm_stable_release_branch branch exists."
|
||||
git checkout litellm_stable_release_branch
|
||||
else
|
||||
echo "litellm_stable_release_branch branch does not exist. Creating it."
|
||||
git checkout -b litellm_stable_release_branch
|
||||
fi
|
||||
|
||||
# Reset litellm_stable_release_branch branch to the release commit
|
||||
git reset --hard $GITHUB_SHA
|
||||
|
||||
# Push the updated litellm_stable_release_branch branch
|
||||
git push origin litellm_stable_release_branch --force
|
||||
67
.github/workflows/simple_pypi_publish.yml
vendored
67
.github/workflows/simple_pypi_publish.yml
vendored
|
|
@ -1,67 +0,0 @@
|
|||
name: Simple PyPI Publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to publish (e.g., 1.74.10)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install toml build wheel twine
|
||||
|
||||
- name: Update version in pyproject.toml
|
||||
run: |
|
||||
python -c "
|
||||
import toml
|
||||
|
||||
with open('pyproject.toml', 'r') as f:
|
||||
data = toml.load(f)
|
||||
|
||||
data['tool']['poetry']['version'] = '${{ github.event.inputs.version }}'
|
||||
|
||||
with open('pyproject.toml', 'w') as f:
|
||||
toml.dump(data, f)
|
||||
|
||||
print(f'Updated version to ${{ github.event.inputs.version }}')
|
||||
"
|
||||
|
||||
- name: Copy model prices file
|
||||
run: |
|
||||
cp model_prices_and_context_window.json litellm/model_prices_and_context_window_backup.json
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
rm -rf build dist
|
||||
python -m build
|
||||
|
||||
- name: Publish to PyPI
|
||||
env:
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_PUBLISH_PASSWORD }}
|
||||
run: |
|
||||
twine upload dist/*
|
||||
|
||||
- name: Output success
|
||||
run: |
|
||||
echo "✅ Successfully published litellm v${{ github.event.inputs.version }} to PyPI"
|
||||
echo "📦 Package: https://pypi.org/project/litellm/${{ github.event.inputs.version }}/"
|
||||
1
.github/workflows/stale.yml
vendored
1
.github/workflows/stale.yml
vendored
|
|
@ -7,6 +7,7 @@ on:
|
|||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
|
|
|
|||
18
.semgrep/rules/security/no-claude-directory.yml
Normal file
18
.semgrep/rules/security/no-claude-directory.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
rules:
|
||||
- id: no-claude-directory-committed
|
||||
message: >
|
||||
.claude/ directory must not be committed to the repository.
|
||||
It contains local Claude Code settings (permissions, worktree paths) that are
|
||||
developer-machine-specific and may expose internal paths or credentials.
|
||||
Add .claude/ to .gitignore instead.
|
||||
severity: ERROR
|
||||
languages: [generic]
|
||||
paths:
|
||||
include:
|
||||
- "/.claude/**"
|
||||
- "/.claude/*"
|
||||
pattern-regex: '[\s\S]+'
|
||||
metadata:
|
||||
category: security
|
||||
tags: [supply-chain, secrets]
|
||||
confidence: HIGH
|
||||
|
|
@ -402,7 +402,7 @@ Support for more providers. Missing a provider or LLM Platform, raise a [feature
|
|||
# Enterprise
|
||||
For companies that need better security, user management and professional support
|
||||
|
||||
[Talk to founders](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Talk to founders](https://enterprise.litellm.ai/demo)
|
||||
|
||||
This covers:
|
||||
- ✅ **Features under the [LiteLLM Commercial License](https://docs.litellm.ai/docs/proxy/enterprise):**
|
||||
|
|
@ -452,7 +452,6 @@ All these checks must pass before your PR can be merged.
|
|||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- [Community Slack 💭](https://www.litellm.ai/support)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
||||
# Why did we build this
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ echo "Starting security scans for LiteLLM..."
|
|||
# Function to install Trivy and required tools
|
||||
install_trivy() {
|
||||
echo "Installing Trivy and required tools..."
|
||||
TRIVY_VERSION="0.35.0"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y wget apt-transport-https gnupg lsb-release jq curl bsdmainutils
|
||||
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
|
||||
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install trivy
|
||||
echo "Trivy and required tools installed successfully"
|
||||
sudo apt-get install -y wget jq curl bsdmainutils
|
||||
wget -qO trivy.deb "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.deb"
|
||||
sudo dpkg -i trivy.deb
|
||||
rm trivy.deb
|
||||
echo "Trivy ${TRIVY_VERSION} installed successfully"
|
||||
}
|
||||
|
||||
# Function to install Grype
|
||||
|
|
|
|||
|
|
@ -178,4 +178,4 @@ Benchmark Results for 'When will BerriAI IPO?':
|
|||
```
|
||||
|
||||
## Support
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://enterprise.litellm.ai/demo) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
|
|
|
|||
|
|
@ -143,7 +143,6 @@ All responses from the server are returned in the following format (for all LLM
|
|||
|
||||
- [Our calendar 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
||||
## Roadmap
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ All responses from the server are returned in the following format (for all LLM
|
|||
|
||||
- [Our calendar 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
||||
## Roadmap
|
||||
|
|
|
|||
709
docs/my-website/blog/security_update_march_2026/index.md
Normal file
709
docs/my-website/blog/security_update_march_2026/index.md
Normal file
|
|
@ -0,0 +1,709 @@
|
|||
---
|
||||
slug: security-update-march-2026
|
||||
title: "Security Update: Suspected Supply Chain Incident"
|
||||
date: 2026-03-24T14:00:00
|
||||
authors:
|
||||
- krrish
|
||||
- ishaan-alt
|
||||
description: "As of 2:00 PM ET on March 24, 2026"
|
||||
tags: [security, incident-report]
|
||||
hide_table_of_contents: false
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
> **Status:** Active investigation
|
||||
> **Last updated:** March 25, 2026
|
||||
|
||||
> **Update (March 25):** Added community-contributed scripts for scanning GitHub Actions and GitLab CI pipelines for the compromised versions. See [How to check if you are affected](#how-to-check-if-you-are-affected). s/o [@Zach Fury](https://www.linkedin.com/in/fryware/) for these scripts.
|
||||
|
||||
|
||||
## TLDR;
|
||||
- The compromised PyPI packages were **litellm==1.82.7** and **litellm==1.82.8**. Those packages have now been removed from PyPI.
|
||||
- We believe that the compromise originated from the Trivy dependency used in our CI/CD security scanning workflow.
|
||||
- Customers running the official LiteLLM Proxy Docker image were not impacted. That deployment path pins dependencies in requirements.txt and does not rely on the compromised PyPI packages.
|
||||
- We are pausing new LiteLLM releases until we complete a broader supply-chain review and confirm the release path is safe.
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
LiteLLM AI Gateway is investigating a suspected supply chain attack involving unauthorized PyPI package publishes. Current evidence suggests a maintainer's PyPI account may have been compromised and used to distribute malicious code.
|
||||
|
||||
At this time, we believe this incident may be linked to the broader [Trivy security compromise](https://www.aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know/), in which stolen credentials were reportedly used to gain unauthorized access to the LiteLLM publishing pipeline.
|
||||
|
||||
This investigation is ongoing. Details below may change as we confirm additional findings.
|
||||
|
||||
## Confirmed affected versions
|
||||
|
||||
The following LiteLLM versions published to PyPI were impacted:
|
||||
|
||||
- **v1.82.7**: contained a malicious payload in the LiteLLM AI Gateway `proxy_server.py`
|
||||
- **v1.82.8**: contained `litellm_init.pth` and a malicious payload in the LiteLLM AI Gateway `proxy_server.py`
|
||||
|
||||
If you installed or ran either of these versions, review the recommendations below immediately.
|
||||
|
||||
Note: These versions have already been removed from PyPI.
|
||||
|
||||
## What happened
|
||||
|
||||
Initial evidence suggests the attacker bypassed official CI/CD workflows and uploaded malicious packages directly to PyPI.
|
||||
|
||||
These compromised versions appear to have included a credential stealer designed to:
|
||||
|
||||
- Harvest secrets by scanning for:
|
||||
- environment variables
|
||||
- SSH keys
|
||||
- cloud provider credentials (AWS, GCP, Azure)
|
||||
- Kubernetes tokens
|
||||
- database passwords
|
||||
- Encrypt and exfiltrate data via a `POST` request to `models.litellm.cloud`, which is **not** an official BerriAI / LiteLLM domain
|
||||
|
||||
## Who is affected
|
||||
|
||||
You may be affected if **any** of the following are true:
|
||||
|
||||
- You installed or upgraded LiteLLM via `pip` on **March 24, 2026**, between **10:39 UTC and 16:00 UTC**
|
||||
- You ran `pip install litellm` without pinning a version and received **v1.82.7** or **v1.82.8**
|
||||
- You built a Docker image during this window that included `pip install litellm` without a pinned version
|
||||
- A dependency in your project pulled in LiteLLM as a transitive, unpinned dependency
|
||||
(for example through AI agent frameworks, MCP servers, or LLM orchestration tools)
|
||||
|
||||
You are **not** affected if any of the following are true:
|
||||
|
||||
**LiteLLM AI Gateway/Proxy users:** Customers running the official LiteLLM Proxy Docker image were not impacted. That deployment path pins dependencies in requirements.txt and does not rely on the compromised PyPI packages.
|
||||
|
||||
- You are using **LiteLLM Cloud**
|
||||
- You are using the official LiteLLM AI Gateway Docker image: `ghcr.io/berriai/litellm`
|
||||
- You are on **v1.82.6 or earlier** and did not upgrade during the affected window
|
||||
- You installed LiteLLM from source via the GitHub repository, which was **not** compromised
|
||||
|
||||
|
||||
### How to check if you are affected
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="sdk" label="SDK">
|
||||
|
||||
```bash
|
||||
pip show litellm
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="proxy" label="PROXY">
|
||||
|
||||
Go to the proxy base url, and check the version of the installed LiteLLM.
|
||||
|
||||

|
||||
</TabItem>
|
||||
<TabItem value="github" label="GitHub Actions">
|
||||
|
||||
Scans all repositories in a GitHub organization for workflow jobs that installed the compromised versions.
|
||||
|
||||
**Requirements:** Python 3 and `requests` (`pip install requests`).
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN="your-github-pat"
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
```bash
|
||||
python find_litellm_github.py
|
||||
```
|
||||
|
||||
Set the `ORG` variable in the script to your GitHub organization name.
|
||||
|
||||
Both scripts default to scanning jobs from **today**. Adjust the `WINDOW_START` and `WINDOW_END` constants to cover **March 24, 2026** (the incident date) if running on a different day.
|
||||
|
||||
<details>
|
||||
<summary>View full script (find_litellm_github.py)</summary>
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Scan all GitHub Actions jobs in a GitHub org that ran between
|
||||
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
|
||||
|
||||
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
|
||||
"""
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import requests
|
||||
|
||||
GITHUB_URL = "https://api.github.com"
|
||||
ORG = "your-org" # <-- set to your GitHub organization
|
||||
TOKEN = os.environ.get("GITHUB_TOKEN", "")
|
||||
|
||||
TODAY = datetime.now(timezone.utc).date()
|
||||
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
|
||||
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
|
||||
|
||||
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
|
||||
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
|
||||
|
||||
SESSION = requests.Session()
|
||||
SESSION.headers.update({
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
})
|
||||
|
||||
|
||||
def get_paginated(url, params=None):
|
||||
params = dict(params or {})
|
||||
params.setdefault("per_page", 100)
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
if resp.status_code == 404:
|
||||
return
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if isinstance(data, dict):
|
||||
items = next((v for v in data.values() if isinstance(v, list)), [])
|
||||
else:
|
||||
items = data
|
||||
if not items:
|
||||
break
|
||||
yield from items
|
||||
if len(items) < params["per_page"]:
|
||||
break
|
||||
page += 1
|
||||
|
||||
|
||||
def parse_ts(ts_str):
|
||||
if not ts_str:
|
||||
return None
|
||||
return datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
|
||||
|
||||
|
||||
def get_repos():
|
||||
repos = []
|
||||
for r in get_paginated(f"{GITHUB_URL}/orgs/{ORG}/repos", {"type": "all"}):
|
||||
repos.append({"id": r["id"], "name": r["name"], "full_name": r["full_name"]})
|
||||
return repos
|
||||
|
||||
|
||||
def get_runs_in_window(repo_full_name):
|
||||
created_filter = (
|
||||
f"{WINDOW_START.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
||||
f"..{WINDOW_END.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
||||
)
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs"
|
||||
runs = []
|
||||
for run in get_paginated(url, {"created": created_filter, "per_page": 100}):
|
||||
ts = parse_ts(run.get("run_started_at") or run.get("created_at"))
|
||||
if ts and WINDOW_START <= ts <= WINDOW_END:
|
||||
runs.append(run)
|
||||
return runs
|
||||
|
||||
|
||||
def get_jobs_for_run(repo_full_name, run_id):
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs/{run_id}/jobs"
|
||||
jobs = []
|
||||
for job in get_paginated(url, {"filter": "all"}):
|
||||
ts = parse_ts(job.get("started_at"))
|
||||
if ts and WINDOW_START <= ts <= WINDOW_END:
|
||||
jobs.append(job)
|
||||
return jobs
|
||||
|
||||
|
||||
def fetch_job_log(repo_full_name, job_id):
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/jobs/{job_id}/logs"
|
||||
resp = SESSION.get(url, timeout=60, allow_redirects=True)
|
||||
if resp.status_code in (403, 404, 410):
|
||||
return ""
|
||||
resp.raise_for_status()
|
||||
|
||||
content_type = resp.headers.get("Content-Type", "")
|
||||
if "zip" in content_type or resp.content[:2] == b"PK":
|
||||
try:
|
||||
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
|
||||
parts = []
|
||||
for name in sorted(zf.namelist()):
|
||||
with zf.open(name) as f:
|
||||
parts.append(f.read().decode("utf-8", errors="replace"))
|
||||
return "\n".join(parts)
|
||||
except zipfile.BadZipFile:
|
||||
pass
|
||||
return resp.text
|
||||
|
||||
|
||||
def check_job(repo_full_name, job):
|
||||
job_id = job["id"]
|
||||
job_name = job["name"]
|
||||
run_id = job["run_id"]
|
||||
started = job.get("started_at", "")
|
||||
|
||||
log_text = fetch_job_log(repo_full_name, job_id)
|
||||
if not log_text:
|
||||
return None
|
||||
|
||||
found_versions = set()
|
||||
context_lines = []
|
||||
for line in log_text.splitlines():
|
||||
m = VERSION_PATTERN.search(line)
|
||||
if m:
|
||||
ver = m.group(1)
|
||||
if ver in TARGET_VERSIONS:
|
||||
found_versions.add(ver)
|
||||
context_lines.append(line.strip())
|
||||
|
||||
if not found_versions:
|
||||
return None
|
||||
|
||||
return {
|
||||
"repo": repo_full_name,
|
||||
"run_id": run_id,
|
||||
"job_id": job_id,
|
||||
"job_name": job_name,
|
||||
"started_at": started,
|
||||
"versions": sorted(found_versions),
|
||||
"context": context_lines[:10],
|
||||
"job_url": job.get("html_url", f"https://github.com/{repo_full_name}/actions/runs/{run_id}"),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print("ERROR: Set GITHUB_TOKEN environment variable.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
|
||||
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
|
||||
print()
|
||||
|
||||
print(f"Fetching repositories for org '{ORG}'...")
|
||||
repos = get_repos()
|
||||
print(f" Found {len(repos)} repositories")
|
||||
print()
|
||||
|
||||
jobs_to_check = []
|
||||
|
||||
print("Scanning workflow runs for time window...")
|
||||
for repo in repos:
|
||||
full_name = repo["full_name"]
|
||||
try:
|
||||
runs = get_runs_in_window(full_name)
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: {full_name} - {e}", file=sys.stderr)
|
||||
continue
|
||||
if not runs:
|
||||
continue
|
||||
print(f" {full_name}: {len(runs)} run(s) in window")
|
||||
for run in runs:
|
||||
try:
|
||||
jobs = get_jobs_for_run(full_name, run["id"])
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: run {run['id']} - {e}", file=sys.stderr)
|
||||
continue
|
||||
for job in jobs:
|
||||
jobs_to_check.append((full_name, job))
|
||||
|
||||
total = len(jobs_to_check)
|
||||
print(f"\nFetching logs for {total} job(s)...")
|
||||
print()
|
||||
|
||||
hits = []
|
||||
with ThreadPoolExecutor(max_workers=8) as pool:
|
||||
futures = {
|
||||
pool.submit(check_job, full_name, job): (full_name, job["id"])
|
||||
for full_name, job in jobs_to_check
|
||||
}
|
||||
done = 0
|
||||
for future in as_completed(futures):
|
||||
done += 1
|
||||
full_name, jid = futures[future]
|
||||
try:
|
||||
result = future.result()
|
||||
except Exception as e:
|
||||
print(f" ERROR {full_name} job {jid}: {e}", file=sys.stderr)
|
||||
continue
|
||||
if result:
|
||||
hits.append(result)
|
||||
print(
|
||||
f" [{done}/{total}] {full_name} job {jid}" +
|
||||
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print()
|
||||
print("=" * 72)
|
||||
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
|
||||
print("=" * 72)
|
||||
|
||||
if not hits:
|
||||
print("No matches found.")
|
||||
return
|
||||
|
||||
for h in sorted(hits, key=lambda x: x["started_at"]):
|
||||
print()
|
||||
print(f" Repo : {h['repo']}")
|
||||
print(f" Job : {h['job_name']} (#{h['job_id']})")
|
||||
print(f" Run ID : {h['run_id']}")
|
||||
print(f" Started : {h['started_at']}")
|
||||
print(f" Versions : litellm {', '.join(h['versions'])}")
|
||||
print(f" URL : {h['job_url']}")
|
||||
print(f" Log lines :")
|
||||
for line in h["context"]:
|
||||
print(f" {line}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="gitlab" label="GitLab CI">
|
||||
|
||||
Scans all projects in a GitLab group (including subgroups) for CI/CD jobs that installed the compromised versions.
|
||||
|
||||
**Requirements:** Python 3 and `requests` (`pip install requests`).
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
export GITLAB_TOKEN="your-gitlab-pat"
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
```bash
|
||||
python find_litellm_jobs.py
|
||||
```
|
||||
|
||||
Set the `GROUP_NAME` variable in the script to your GitLab group name.
|
||||
|
||||
Both scripts default to scanning jobs from **today**. Adjust the `WINDOW_START` and `WINDOW_END` constants to cover **March 24, 2026** (the incident date) if running on a different day.
|
||||
|
||||
<details>
|
||||
<summary>View full script (find_litellm_jobs.py)</summary>
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Scan all GitLab CI/CD jobs in a GitLab group that ran between
|
||||
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
|
||||
|
||||
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import requests
|
||||
|
||||
GITLAB_URL = "https://gitlab.com"
|
||||
GROUP_NAME = "YourGroup" # <-- set to your GitLab group name
|
||||
TOKEN = os.environ.get("GITLAB_TOKEN", "")
|
||||
|
||||
TODAY = datetime.now(timezone.utc).date()
|
||||
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
|
||||
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
|
||||
|
||||
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
|
||||
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
|
||||
|
||||
HEADERS = {"PRIVATE-TOKEN": TOKEN}
|
||||
SESSION = requests.Session()
|
||||
SESSION.headers.update(HEADERS)
|
||||
|
||||
|
||||
def get_paginated(url, params=None):
|
||||
params = dict(params or {})
|
||||
params.setdefault("per_page", 100)
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if not data:
|
||||
break
|
||||
yield from data
|
||||
if len(data) < params["per_page"]:
|
||||
break
|
||||
page += 1
|
||||
|
||||
|
||||
def get_group_id(group_name):
|
||||
resp = SESSION.get(f"{GITLAB_URL}/api/v4/groups/{group_name}", timeout=30)
|
||||
resp.raise_for_status()
|
||||
return resp.json()["id"]
|
||||
|
||||
|
||||
def get_all_projects(group_id):
|
||||
projects = []
|
||||
for p in get_paginated(
|
||||
f"{GITLAB_URL}/api/v4/groups/{group_id}/projects",
|
||||
{"include_subgroups": "true", "archived": "false"},
|
||||
):
|
||||
projects.append({"id": p["id"], "name": p["path_with_namespace"]})
|
||||
return projects
|
||||
|
||||
|
||||
def parse_ts(ts_str):
|
||||
if not ts_str:
|
||||
return None
|
||||
ts_str = ts_str.replace("Z", "+00:00")
|
||||
return datetime.fromisoformat(ts_str)
|
||||
|
||||
|
||||
def jobs_in_window(project_id):
|
||||
matching = []
|
||||
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs"
|
||||
params = {"per_page": 100, "scope[]": ["success", "failed", "canceled", "running"]}
|
||||
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
if resp.status_code == 403:
|
||||
return matching
|
||||
resp.raise_for_status()
|
||||
jobs = resp.json()
|
||||
if not jobs:
|
||||
break
|
||||
|
||||
stop_early = False
|
||||
for job in jobs:
|
||||
ts = parse_ts(job.get("started_at") or job.get("created_at"))
|
||||
if ts is None:
|
||||
continue
|
||||
if ts > WINDOW_END:
|
||||
continue
|
||||
if ts < WINDOW_START:
|
||||
stop_early = True
|
||||
continue
|
||||
matching.append(job)
|
||||
|
||||
if stop_early or len(jobs) < 100:
|
||||
break
|
||||
page += 1
|
||||
|
||||
return matching
|
||||
|
||||
|
||||
def fetch_trace(project_id, job_id):
|
||||
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs/{job_id}/trace"
|
||||
resp = SESSION.get(url, timeout=60)
|
||||
if resp.status_code in (403, 404):
|
||||
return ""
|
||||
resp.raise_for_status()
|
||||
return resp.text
|
||||
|
||||
|
||||
def check_job(project_name, project_id, job):
|
||||
job_id = job["id"]
|
||||
job_name = job["name"]
|
||||
ref = job.get("ref", "")
|
||||
started = job.get("started_at", job.get("created_at", ""))
|
||||
|
||||
trace = fetch_trace(project_id, job_id)
|
||||
if not trace:
|
||||
return None
|
||||
|
||||
found_versions = set()
|
||||
for match in VERSION_PATTERN.finditer(trace):
|
||||
ver = match.group(1)
|
||||
if ver in TARGET_VERSIONS:
|
||||
found_versions.add(ver)
|
||||
|
||||
if not found_versions:
|
||||
return None
|
||||
|
||||
context_lines = []
|
||||
for line in trace.splitlines():
|
||||
if VERSION_PATTERN.search(line):
|
||||
ver_match = VERSION_PATTERN.search(line)
|
||||
if ver_match and ver_match.group(1) in TARGET_VERSIONS:
|
||||
context_lines.append(line.strip())
|
||||
|
||||
return {
|
||||
"project": project_name,
|
||||
"project_id": project_id,
|
||||
"job_id": job_id,
|
||||
"job_name": job_name,
|
||||
"ref": ref,
|
||||
"started_at": started,
|
||||
"versions": sorted(found_versions),
|
||||
"context": context_lines[:10],
|
||||
"job_url": f"{GITLAB_URL}/{project_name}/-/jobs/{job_id}",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print("ERROR: Set GITLAB_TOKEN environment variable.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
|
||||
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
|
||||
print()
|
||||
|
||||
print(f"Resolving group '{GROUP_NAME}'...")
|
||||
group_id = get_group_id(GROUP_NAME)
|
||||
|
||||
print("Fetching projects...")
|
||||
projects = get_all_projects(group_id)
|
||||
print(f" Found {len(projects)} projects")
|
||||
print()
|
||||
|
||||
all_jobs_to_check = []
|
||||
|
||||
print("Scanning job listings for time window...")
|
||||
for proj in projects:
|
||||
try:
|
||||
jobs = jobs_in_window(proj["id"])
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: {proj['name']} - {e}", file=sys.stderr)
|
||||
continue
|
||||
if jobs:
|
||||
print(f" {proj['name']}: {len(jobs)} job(s) in window")
|
||||
for j in jobs:
|
||||
all_jobs_to_check.append((proj["name"], proj["id"], j))
|
||||
|
||||
total = len(all_jobs_to_check)
|
||||
print(f"\nFetching traces for {total} job(s)...")
|
||||
print()
|
||||
|
||||
hits = []
|
||||
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||
futures = {
|
||||
pool.submit(check_job, pname, pid, job): (pname, job["id"])
|
||||
for pname, pid, job in all_jobs_to_check
|
||||
}
|
||||
done = 0
|
||||
for future in as_completed(futures):
|
||||
done += 1
|
||||
pname, jid = futures[future]
|
||||
try:
|
||||
result = future.result()
|
||||
except Exception as e:
|
||||
print(f" ERROR checking {pname} job {jid}: {e}", file=sys.stderr)
|
||||
continue
|
||||
if result:
|
||||
hits.append(result)
|
||||
print(f" [{done}/{total}] checked {pname} job {jid}" +
|
||||
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
|
||||
flush=True)
|
||||
|
||||
print()
|
||||
print("=" * 72)
|
||||
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
|
||||
print("=" * 72)
|
||||
|
||||
if not hits:
|
||||
print("No matches found.")
|
||||
return
|
||||
|
||||
for h in sorted(hits, key=lambda x: x["started_at"]):
|
||||
print()
|
||||
print(f" Project : {h['project']}")
|
||||
print(f" Job : {h['job_name']} (#{h['job_id']})")
|
||||
print(f" Branch/tag: {h['ref']}")
|
||||
print(f" Started : {h['started_at']}")
|
||||
print(f" Versions : litellm {', '.join(h['versions'])}")
|
||||
print(f" URL : {h['job_url']}")
|
||||
print(f" Log lines :")
|
||||
for line in h["context"]:
|
||||
print(f" {line}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
*CI/CD scripts contributed by the community ([original gist](https://gist.github.com/fryz/93ec8d4898ffe5b5ac5706a208823ef3)). Review before running.*
|
||||
|
||||
|
||||
## Indicators of compromise (IoCs)
|
||||
|
||||
Review affected systems for the following indicators:
|
||||
|
||||
- `litellm_init.pth` present in your `site-packages`
|
||||
- Outbound traffic or requests to `models.litellm[.]cloud`
|
||||
This domain is **not** affiliated with LiteLLM
|
||||
|
||||
|
||||
## Immediate actions for affected users
|
||||
|
||||
If you installed or ran **v1.82.7** or **v1.82.8**, take the following actions immediately.
|
||||
|
||||
### 1. Rotate all secrets
|
||||
|
||||
Treat any credentials present on the affected systems as compromised, including:
|
||||
|
||||
- API keys
|
||||
- Cloud access keys
|
||||
- Database passwords
|
||||
- SSH keys
|
||||
- Kubernetes tokens
|
||||
- Any secrets stored in environment variables or configuration files
|
||||
|
||||
### 2. Inspect your filesystem
|
||||
|
||||
Check your `site-packages` directory for a file named `litellm_init.pth`:
|
||||
|
||||
```bash
|
||||
find /usr/lib/python3.13/site-packages/ -name "litellm_init.pth"
|
||||
```
|
||||
|
||||
If present:
|
||||
|
||||
- remove it immediately
|
||||
- investigate the host for further compromise
|
||||
- preserve relevant artifacts if your security team is performing forensics
|
||||
|
||||
### 3. Audit version history
|
||||
|
||||
Review your:
|
||||
|
||||
- Local environments
|
||||
- CI/CD pipelines
|
||||
- Docker builds
|
||||
- Deployment logs
|
||||
|
||||
Confirm whether **v1.82.7** or **v1.82.8** was installed anywhere.
|
||||
|
||||
Pin LiteLLM to a known safe version such as **v1.82.6 or earlier**, or to a later verified release once announced.
|
||||
|
||||
|
||||
## Response and remediation
|
||||
|
||||
The LiteLLM AI Gateway team has already taken the following steps:
|
||||
|
||||
- Removed compromised packages from PyPI
|
||||
- Rotated maintainer credentials and established new authorized maintainers
|
||||
- Engaged Google's Mandiant security team to assist with forensic analysis of the build and publishing chain
|
||||
|
||||
|
||||
## Questions and support
|
||||
|
||||
If you believe your systems may be affected, contact us immediately:
|
||||
|
||||
- **Security:** `security@berri.ai`
|
||||
- **Support:** `support@berri.ai`
|
||||
- **Slack:** Reach out to the LiteLLM team directly
|
||||
|
||||
For real-time updates, follow [LiteLLM (YC W23) on X](https://x.com/LiteLLM).
|
||||
|
||||
|
|
@ -128,8 +128,6 @@ We'll review all reports promptly. Note that we don't currently offer a bug boun
|
|||
|
||||
Legal Entity Name: Berrie AI Incorporated
|
||||
|
||||
Company Phone Number: 7708783106
|
||||
|
||||
Point of contact email address for security incidents: krrish@berri.ai
|
||||
|
||||
Point of contact email address for general security-related questions: krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -67,6 +67,6 @@ response = completion("command-nightly", messages, logger_fn=my_custom_logging_f
|
|||
|
||||
## Still Seeing Issues?
|
||||
|
||||
Text us @ +17708783106 or Join the [Discord](https://discord.com/invite/wuPM9dRgDw).
|
||||
Join the [Discord](https://discord.com/invite/wuPM9dRgDw).
|
||||
|
||||
We promise to help you in `lite`ning speed ❤️
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Image from '@theme/IdealImage';
|
|||
|
||||
:::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)
|
||||
- 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/cx9p-5yf-2nm/litellm-introductions) to discuss your needs.
|
||||
- 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://enterprise.litellm.ai/demo) to discuss your needs.
|
||||
:::
|
||||
|
||||
For companies that need SSO, user management and professional support for LiteLLM Proxy
|
||||
|
|
@ -36,7 +36,7 @@ Manage Yourself - you can deploy our Docker Image or build a custom image from o
|
|||
|
||||
### What’s the cost of the Self-Managed Enterprise edition?
|
||||
|
||||
Self-Managed Enterprise deployments require our team to understand your exact needs. [Get in touch with us to learn more](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Self-Managed Enterprise deployments require our team to understand your exact needs. [Get in touch with us to learn more](https://enterprise.litellm.ai/demo)
|
||||
|
||||
|
||||
### How does deployment with Enterprise License work?
|
||||
|
|
@ -106,7 +106,7 @@ Professional Support can assist with LLM/Provider integrations, deployment, upgr
|
|||
|
||||
Pricing is based on usage. We can figure out a price that works for your team, on the call.
|
||||
|
||||
[**Contact Us to learn more**](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[**Contact Us to learn more**](https://enterprise.litellm.ai/demo)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
:::info
|
||||
|
||||
This is an Enterprise only endpoint [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
This is an Enterprise only endpoint [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -31,5 +31,4 @@ When we have breaking changes (i.e. going from 1.x.x to 2.x.x), we will document
|
|||
**How can we communicate changes better?**
|
||||
Tell us
|
||||
- [Discord](https://discord.com/invite/wuPM9dRgDw)
|
||||
- Email (krrish@berri.ai/ishaan@berri.ai)
|
||||
- Text us (+17708783106)
|
||||
- Email (support@berri.ai)
|
||||
|
|
|
|||
|
|
@ -194,5 +194,4 @@ print(response)
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Log LLM Logs to [Google Cloud Storage Buckets](https://cloud.google.com/storage?
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -79,5 +79,4 @@ curl --location 'http://0.0.0.0:4000/chat/completions' \
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -342,5 +342,4 @@ Be aware that if you are continuing an existing trace, and you set `update_trace
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -225,5 +225,4 @@ environment_variables:
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -63,5 +63,4 @@ response = litellm.completion(
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -176,5 +176,4 @@ You can find more details about the different ways of making requests to the Lit
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -261,5 +261,4 @@ All requests made with this key will automatically be tracked in the "TestProjec
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -127,5 +127,4 @@ Depending on which Phoenix Cloud version or deployment you are using, you should
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -84,5 +84,4 @@ Credits to [Nick Bradford](https://github.com/nsbradford), from [Vim-GPT](https:
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
@ -101,5 +101,4 @@ response = litellm.completion(
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -328,5 +328,4 @@ If you get authentication errors, regenerate the HTTP Source URL in Sumo Logic:
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -105,5 +105,4 @@ litellm.modify_integration("supabase",{"table_name": "litellm_logs"})
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -57,5 +57,4 @@ response = litellm.completion(
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
@ -206,7 +206,7 @@ router_settings:
|
|||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| completion_model | string | The default model to use for completions when `model` is not specified in the request |
|
||||
| completion_model | string | The model to use for all completions, overriding any `model` specified in the request |
|
||||
| disable_spend_logs | boolean | If true, turns off writing each transaction to the database |
|
||||
| disable_spend_updates | boolean | If true, turns off all spend updates to the DB. Including key/user/team spend updates. |
|
||||
| disable_master_key_return | boolean | If true, turns off returning master key on UI. (checked on '/user/info' endpoint) |
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ Use this when you want non-proxy admins to access `/spend` endpoints
|
|||
|
||||
:::info
|
||||
|
||||
Schedule a [meeting with us to get your Enterprise License](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Schedule a [meeting with us to get your Enterprise License](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ After regenerating the key, the user will receive an email notification with:
|
|||
|
||||
:::info
|
||||
|
||||
Customizing Email Branding is an Enterprise Feature [Get in touch with us for a Free Trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Customizing Email Branding is an Enterprise Feature [Get in touch with us for a Free Trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
|||
# ✨ Enterprise Features
|
||||
:::tip
|
||||
|
||||
To get a license, get in touch with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
To get a license, get in touch with us [here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ curl -i -X POST http://localhost:4000/v1/chat/completions \
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ curl -i http://localhost:4000/v1/chat/completions \
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
:::info
|
||||
|
||||
You need a LiteLLM License to unlock this feature. [Grab time](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions), to get one today!
|
||||
You need a LiteLLM License to unlock this feature. [Grab time](https://enterprise.litellm.ai/demo), to get one today!
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -1109,7 +1109,7 @@ Log LLM Logs to [Google Cloud Storage Buckets](https://cloud.google.com/storage?
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -1194,7 +1194,7 @@ Log LLM Logs/SpendLogs to [Google Cloud Storage PubSub Topic](https://cloud.goog
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -1497,7 +1497,7 @@ Log LLM Logs to [Azure Data Lake Storage](https://learn.microsoft.com/en-us/azur
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ LiteLLM tracks changes to the following entities and actions:
|
|||
|
||||
:::tip
|
||||
|
||||
Requires Enterprise License, Get in touch with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Requires Enterprise License, Get in touch with us [here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -56,6 +56,40 @@ On the LiteLLM UI, navigate to Logs -> Audit Logs. You should see the audit log
|
|||
/>
|
||||
|
||||
|
||||
## Export Audit Logs to External Storage
|
||||
|
||||
You can export audit logs to an external storage backend (e.g. S3) in addition to storing them in the database. Logs are batched and uploaded asynchronously, so they do not block your proxy requests.
|
||||
|
||||
### S3 Example
|
||||
|
||||
Add `audit_log_callbacks` and `s3_callback_params` to your `litellm_settings`:
|
||||
|
||||
```yaml
|
||||
litellm_settings:
|
||||
store_audit_logs: true
|
||||
audit_log_callbacks: ["s3_v2"]
|
||||
s3_callback_params:
|
||||
s3_bucket_name: my-audit-logs-bucket # AWS Bucket Name
|
||||
s3_region_name: us-west-2 # AWS Region
|
||||
s3_aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID
|
||||
s3_aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY
|
||||
s3_path: litellm-audit # [OPTIONAL] prefix path in the bucket
|
||||
```
|
||||
|
||||
Audit logs are written as JSON files to:
|
||||
|
||||
```
|
||||
s3://<bucket>/audit_logs/<YYYY-MM-DD>/<HH-MM-SS>_<audit-log-id>.json
|
||||
# or, when s3_path is set:
|
||||
s3://<bucket>/<s3_path>/audit_logs/<YYYY-MM-DD>/<HH-MM-SS>_<audit-log-id>.json
|
||||
```
|
||||
|
||||
:::info
|
||||
|
||||
Both `store_audit_logs: true` and `audit_log_callbacks` must be set. If `store_audit_logs` is not enabled, the callbacks will not fire.
|
||||
|
||||
:::
|
||||
|
||||
## Advanced
|
||||
|
||||
### Attribute Management changes to Users
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Use this if you want to use an Oauth2.0 token to make `/chat`, `/embeddings` req
|
|||
|
||||
:::info
|
||||
|
||||
This is an Enterprise Feature - [get in touch with us if you want a free trial to test if this feature meets your needs]((https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions))
|
||||
This is an Enterprise Feature - [get in touch with us if you want a free trial to test if this feature meets your needs]((https://enterprise.litellm.ai/demo))
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -422,6 +422,5 @@ general_settings:
|
|||
|
||||
[Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
|
||||
Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
|
||||
Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export LITELLM_LOG="ERROR"
|
|||
|
||||
:::info
|
||||
|
||||
Need Help or want dedicated support ? Talk to a founder [here]: (https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Need Help or want dedicated support ? Talk to a founder [here]: (https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
:::info
|
||||
|
||||
Requires a LiteLLM Enterprise License. [Get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions).
|
||||
Requires a LiteLLM Enterprise License. [Get a free trial](https://enterprise.litellm.ai/demo).
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ LiteLLM Proxy supports team-based tag routing, allowing you to associate specifi
|
|||
|
||||
:::info
|
||||
|
||||
This is an enterprise feature, [Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
This is an enterprise feature, [Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Team 3 -> Disabled Logging (for GDPR compliance)
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
@ -248,7 +248,7 @@ Use the `/key/generate` or `/key/update` endpoints to add logging callbacks to a
|
|||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
This is an Enterprise feature.
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Use JWT's to auth admins / users / projects into the proxy.
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -813,5 +813,4 @@ Thread Stats Avg Stdev Max +/- Stdev
|
|||
|
||||
- [Schedule Demo 👋](https://calendly.com/d/4mp-gd3-k5k/berriai-1-1-onboarding-litellm-hosted-version)
|
||||
- [Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
- Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
- Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ Supported Providers:
|
|||
- Deepseek (`deepseek/`)
|
||||
- Anthropic API (`anthropic/`)
|
||||
- Bedrock (Anthropic + Deepseek + GPT-OSS) (`bedrock/`)
|
||||
- OpenAI Responses API (`openai/responses/`)
|
||||
- Vertex AI (Anthropic) (`vertexai/`)
|
||||
- OpenRouter (`openrouter/`)
|
||||
- XAI (`xai/`)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem';
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
|||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ Full error logs, stack traces, and any images from service metrics (CPU, memory,
|
|||
[Community Discord 💭](https://discord.gg/wuPM9dRgDw)
|
||||
[Community Slack 💭](https://www.litellm.ai/support)
|
||||
|
||||
Our numbers 📞 +1 (770) 8783-106 / +1 (412) 618-6238
|
||||
|
||||
Our emails ✉️ ishaan@berri.ai / krrish@berri.ai
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ Benchmark Results for 'When will BerriAI IPO?':
|
|||
+-----------------+----------------------------------------------------------------------------------+---------------------------+------------+
|
||||
```
|
||||
## Support
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://enterprise.litellm.ai/demo) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
|
||||
|
||||
<!--
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ model_list:
|
|||
litellm_params:
|
||||
model: openai/gpt-4
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
|
||||
- model_name: gpt-4o
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
|
|
@ -264,7 +264,7 @@ model_list:
|
|||
litellm_params:
|
||||
model: anthropic/claude-3-5-sonnet-20241022
|
||||
api_key: os.environ/ANTHROPIC_API_KEY
|
||||
|
||||
|
||||
# DeepSeek models
|
||||
- model_name: deepseek-chat
|
||||
litellm_params:
|
||||
|
|
@ -272,6 +272,19 @@ model_list:
|
|||
api_key: os.environ/DEEPSEEK_API_KEY
|
||||
```
|
||||
|
||||
### Dropping OpenCode-specific parameters
|
||||
|
||||
OpenCode sends a `reasoningSummary` parameter with reasoning-capable models such as `gpt-5`. This parameter is not supported by the Chat Completions API and will cause errors. Add `additional_drop_params` to every model entry in your `model_list` that will receive requests from OpenCode with reasoning enabled:
|
||||
|
||||
```yaml
|
||||
model_list:
|
||||
- model_name: gpt-5
|
||||
litellm_params:
|
||||
model: openai/gpt-5
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
additional_drop_params: ["reasoningSummary"]
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**OpenCode not connecting:**
|
||||
|
|
@ -294,6 +307,16 @@ model_list:
|
|||
- Validate JSON syntax using a JSON validator
|
||||
- Ensure the `$schema` URL is accessible
|
||||
|
||||
**`Unknown parameter: 'reasoningSummary'` error:**
|
||||
- OpenCode sends a `reasoningSummary` parameter that is not supported by the Chat Completions API. Add `additional_drop_params: ["reasoningSummary"]` to each affected model entry in your `litellm_params`:
|
||||
```yaml
|
||||
- model_name: gpt-5
|
||||
litellm_params:
|
||||
model: openai/gpt-5
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
additional_drop_params: ["reasoningSummary"]
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
- Add more models to the config as needed - they'll appear in `/models`
|
||||
|
|
|
|||
BIN
docs/my-website/img/security_update_march_2026/proxy_version.png
Normal file
BIN
docs/my-website/img/security_update_march_2026/proxy_version.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 396 KiB |
|
|
@ -7,7 +7,7 @@ With regard to the BerriAI Software:
|
|||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the BerriAI Subscription Terms of Service, available
|
||||
via [call](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) or email (info@berri.ai) (the "Enterprise Terms"), or other
|
||||
via [call](https://enterprise.litellm.ai/demo) or email (info@berri.ai) (the "Enterprise Terms"), or other
|
||||
agreement governing the use of the Software, as agreed by you and BerriAI,
|
||||
and otherwise have a valid BerriAI Enterprise license for the
|
||||
correct number of user seats. Subject to the foregoing sentence, you are free to
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@ Code in this folder is licensed under a commercial license. Please review the [L
|
|||
|
||||
**These features are covered under the LiteLLM Enterprise contract**
|
||||
|
||||
👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions?month=2024-02)
|
||||
👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://enterprise.litellm.ai/demo?month=2024-02)
|
||||
|
||||
See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise)
|
||||
|
|
|
|||
|
|
@ -632,7 +632,7 @@ class BaseEmailLogger(CustomLogger):
|
|||
warning_msg = (
|
||||
f"Email sent with default values instead of custom values for: {fields_str}. "
|
||||
"This is an Enterprise feature. To use custom email fields, please upgrade to LiteLLM Enterprise. "
|
||||
"Schedule a meeting here: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions"
|
||||
"Schedule a meeting here: https://enterprise.litellm.ai/demo"
|
||||
)
|
||||
verbose_proxy_logger.warning(f"{warning_msg}")
|
||||
|
||||
|
|
|
|||
|
|
@ -931,10 +931,14 @@ class PrometheusLogger(CustomLogger):
|
|||
user_api_key_auth_metadata: Optional[dict] = standard_logging_payload[
|
||||
"metadata"
|
||||
].get("user_api_key_auth_metadata")
|
||||
spend_logs_metadata: Optional[dict] = standard_logging_payload["metadata"].get(
|
||||
"spend_logs_metadata"
|
||||
)
|
||||
|
||||
combined_metadata: Dict[str, Any] = {
|
||||
**(_requester_metadata if _requester_metadata else {}),
|
||||
**(user_api_key_auth_metadata if user_api_key_auth_metadata else {}),
|
||||
**(spend_logs_metadata if spend_logs_metadata else {}),
|
||||
}
|
||||
if standard_logging_payload is not None and isinstance(
|
||||
standard_logging_payload, dict
|
||||
|
|
|
|||
|
|
@ -4753,6 +4753,7 @@ class StandardLoggingPayloadSetup:
|
|||
user_api_key_team_id=None,
|
||||
user_api_key_org_id=None,
|
||||
user_api_key_project_id=None,
|
||||
user_api_key_project_alias=None,
|
||||
user_api_key_user_id=None,
|
||||
user_api_key_team_alias=None,
|
||||
user_api_key_user_email=None,
|
||||
|
|
@ -5584,6 +5585,7 @@ def get_standard_logging_metadata(
|
|||
user_api_key_team_id=None,
|
||||
user_api_key_org_id=None,
|
||||
user_api_key_project_id=None,
|
||||
user_api_key_project_alias=None,
|
||||
user_api_key_user_id=None,
|
||||
user_api_key_user_email=None,
|
||||
user_api_key_team_alias=None,
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ class A2AGuardrailHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
user_api_key_dict: Optional["UserAPIKeyAuth"] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process A2A output response by applying guardrails to text content.
|
||||
|
|
@ -166,13 +167,21 @@ class A2AGuardrailHandler(BaseTranslation):
|
|||
return response
|
||||
|
||||
# Step 2: Apply guardrail to all texts in batch
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response_dict}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response_dict}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response_dict
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(user_api_key_dict)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
|
||||
|
|
@ -213,6 +222,7 @@ class A2AGuardrailHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
user_api_key_dict: Optional["UserAPIKeyAuth"] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> List[Any]:
|
||||
"""
|
||||
Process A2A streaming output by applying guardrails to accumulated text.
|
||||
|
|
@ -258,10 +268,18 @@ class A2AGuardrailHandler(BaseTranslation):
|
|||
if not combined_text:
|
||||
return responses_so_far
|
||||
|
||||
request_data: dict = {"responses_so_far": responses_so_far}
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(user_api_key_dict)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if request_data is None:
|
||||
request_data = {"responses_so_far": responses_so_far}
|
||||
else:
|
||||
if "responses_so_far" not in request_data:
|
||||
request_data["responses_so_far"] = responses_so_far
|
||||
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=[combined_text])
|
||||
guardrailed_inputs = await guardrail_to_apply.apply_guardrail(
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response by applying guardrails to text content and tool calls.
|
||||
|
|
@ -323,15 +324,21 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
|
||||
# Step 2: Apply guardrail to all texts in batch
|
||||
if texts_to_check or tool_calls_to_check:
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
if images_to_check:
|
||||
|
|
@ -375,6 +382,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> List[Any]:
|
||||
"""
|
||||
Process output streaming response by applying guardrails to text content.
|
||||
|
|
@ -413,7 +421,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
|
||||
_guardrailed_inputs = await guardrail_to_apply.apply_guardrail( # allow rejecting the response, if invalid
|
||||
inputs=guardrail_inputs,
|
||||
request_data={},
|
||||
request_data=request_data if request_data is not None else {},
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
@ -426,7 +434,7 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
string_so_far = self.get_streaming_string_so_far(responses_so_far)
|
||||
_guardrailed_inputs = await guardrail_to_apply.apply_guardrail( # allow rejecting the response, if invalid
|
||||
inputs={"texts": [string_so_far]},
|
||||
request_data={},
|
||||
request_data=request_data if request_data is not None else {},
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class BaseTranslation(ABC):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
user_api_key_dict: Optional["UserAPIKeyAuth"] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response with guardrails.
|
||||
|
|
@ -91,6 +92,7 @@ class BaseTranslation(ABC):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
user_api_key_dict: Optional["UserAPIKeyAuth"] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output streaming response with guardrails.
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class CohereRerankHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response - not applicable for rerank.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ class OCRHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process OCR output by applying guardrails to extracted page text.
|
||||
|
|
@ -127,14 +128,27 @@ class OCRHandler(BaseTranslation):
|
|||
if model:
|
||||
inputs["model"] = model
|
||||
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {}
|
||||
|
||||
# Add user metadata if available
|
||||
if user_api_key_dict is not None:
|
||||
metadata = self.transform_user_api_key_dict_to_metadata(user_api_key_dict)
|
||||
inputs.update(metadata) # type: ignore
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
# Preserve original behavior: inject metadata into inputs for
|
||||
# third-party guardrail providers that read it from there
|
||||
inputs.update(user_metadata) # type: ignore
|
||||
# Also store in request_data for the logging pipeline
|
||||
if "litellm_metadata" not in request_data:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
guardrailed_inputs = await guardrail_to_apply.apply_guardrail(
|
||||
inputs=inputs,
|
||||
request_data={},
|
||||
request_data=request_data,
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -86,9 +86,9 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
if tool_calls_to_check:
|
||||
inputs["tool_calls"] = tool_calls_to_check # type: ignore
|
||||
if messages:
|
||||
inputs[
|
||||
"structured_messages"
|
||||
] = messages # pass the openai /chat/completions messages to the guardrail, as-is
|
||||
inputs["structured_messages"] = (
|
||||
messages # pass the openai /chat/completions messages to the guardrail, as-is
|
||||
)
|
||||
# Pass tools (function definitions) to the guardrail
|
||||
tools = data.get("tools")
|
||||
if tools:
|
||||
|
|
@ -260,6 +260,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response by applying guardrails to text content.
|
||||
|
|
@ -308,15 +309,21 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
|
||||
# Step 2: Apply guardrail to all texts and tool calls in batch
|
||||
if texts_to_check or tool_calls_to_check:
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
if images_to_check:
|
||||
|
|
@ -364,6 +371,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> List["ModelResponseStream"]:
|
||||
"""
|
||||
Process output streaming responses by applying guardrails to text content.
|
||||
|
|
@ -402,6 +410,7 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
guardrail_to_apply=guardrail_to_apply,
|
||||
litellm_logging_obj=litellm_logging_obj,
|
||||
user_api_key_dict=user_api_key_dict,
|
||||
request_data=request_data,
|
||||
)
|
||||
|
||||
return responses_so_far
|
||||
|
|
@ -436,15 +445,21 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
|
|||
|
||||
# Step 3: Apply guardrail to all combined texts in batch
|
||||
if texts_to_check:
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"responses": responses_so_far}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"responses": responses_so_far}
|
||||
else:
|
||||
if "responses" not in request_data:
|
||||
request_data["responses"] = responses_so_far
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
if images_to_check:
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ class OpenAITextCompletionHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response by applying guardrails to completion text.
|
||||
|
|
@ -155,15 +156,21 @@ class OpenAITextCompletionHandler(BaseTranslation):
|
|||
|
||||
# Apply guardrails in batch
|
||||
if texts_to_check:
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
# Include model information from the response if available
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class OpenAIEmbeddingsHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response - embeddings responses contain vectors, not text.
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class OpenAIImageGenerationHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response - typically not needed for image generation.
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response by applying guardrails to text content and tool calls.
|
||||
|
|
@ -402,15 +403,21 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
|
||||
# Step 2: Apply guardrail to all texts in batch
|
||||
if texts_to_check or tool_calls_to_check:
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=texts_to_check)
|
||||
if images_to_check:
|
||||
|
|
@ -454,6 +461,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> List[Any]:
|
||||
"""
|
||||
Process output streaming response by applying guardrails to text content.
|
||||
|
|
@ -481,7 +489,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
inputs["model"] = model_response_stream.model
|
||||
_guardrailed_inputs = await guardrail_to_apply.apply_guardrail(
|
||||
inputs=inputs,
|
||||
request_data={},
|
||||
request_data=request_data if request_data is not None else {},
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
@ -512,7 +520,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
if tool_calls or text:
|
||||
_guardrailed_inputs = await guardrail_to_apply.apply_guardrail(
|
||||
inputs=guardrail_inputs,
|
||||
request_data={},
|
||||
request_data=request_data if request_data is not None else {},
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
@ -537,7 +545,7 @@ class OpenAIResponsesHandler(BaseTranslation):
|
|||
inputs["model"] = response_model
|
||||
_guardrailed_inputs = await guardrail_to_apply.apply_guardrail(
|
||||
inputs=inputs,
|
||||
request_data={},
|
||||
request_data=request_data if request_data is not None else {},
|
||||
input_type="response",
|
||||
logging_obj=litellm_logging_obj,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ class OpenAITextToSpeechHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output - not applicable for text-to-speech.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class OpenAIAudioTranscriptionHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output transcription by applying guardrails to transcribed text.
|
||||
|
|
@ -79,15 +80,21 @@ class OpenAIAudioTranscriptionHandler(BaseTranslation):
|
|||
|
||||
if isinstance(response.text, str):
|
||||
original_text = response.text
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = {"response": response}
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = {"response": response}
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = response
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
inputs = GenericGuardrailAPIInputs(texts=[original_text])
|
||||
# Include model information from the response if available
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ class PassThroughEndpointHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Process output response by applying guardrails to targeted fields.
|
||||
|
|
@ -171,17 +172,27 @@ class PassThroughEndpointHandler(BaseTranslation):
|
|||
if not text_to_check:
|
||||
return response
|
||||
|
||||
# Create a request_data dict with response info and user API key metadata
|
||||
request_data: dict = (
|
||||
{"response": response}
|
||||
if not isinstance(response, dict)
|
||||
else response.copy()
|
||||
)
|
||||
# Use the real request_data if provided (proxy path), otherwise
|
||||
# create a standalone dict (SDK / direct-call path).
|
||||
if request_data is None:
|
||||
request_data = (
|
||||
{"response": response}
|
||||
if not isinstance(response, dict)
|
||||
else response.copy()
|
||||
)
|
||||
else:
|
||||
if "response" not in request_data:
|
||||
request_data["response"] = (
|
||||
response if not isinstance(response, dict) else response.copy()
|
||||
)
|
||||
|
||||
# Add user API key metadata with prefixed keys
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(user_api_key_dict)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
if "litellm_metadata" not in request_data:
|
||||
user_metadata = self.transform_user_api_key_dict_to_metadata(
|
||||
user_api_key_dict
|
||||
)
|
||||
if user_metadata:
|
||||
request_data["litellm_metadata"] = user_metadata
|
||||
|
||||
# Apply guardrail (pass-through doesn't modify the text, just checks it)
|
||||
inputs = GenericGuardrailAPIInputs(texts=[text_to_check])
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class MCPGuardrailTranslationHandler(BaseTranslation):
|
|||
guardrail_to_apply: "CustomGuardrail",
|
||||
litellm_logging_obj: Optional[Any] = None,
|
||||
user_api_key_dict: Optional[Any] = None,
|
||||
request_data: Optional[dict] = None,
|
||||
) -> Any:
|
||||
verbose_proxy_logger.debug(
|
||||
"MCP Guardrail: Output processing not implemented for MCP tools",
|
||||
|
|
|
|||
|
|
@ -2416,6 +2416,7 @@ class LiteLLM_VerificationTokenView(LiteLLM_VerificationToken):
|
|||
organization_metadata: Optional[dict] = None
|
||||
|
||||
# Project Params
|
||||
project_alias: Optional[str] = None
|
||||
project_metadata: Optional[dict] = None
|
||||
|
||||
# Time stamps
|
||||
|
|
@ -3228,6 +3229,7 @@ class SpendLogsMetadata(TypedDict):
|
|||
user_api_key_alias: Optional[str]
|
||||
user_api_key_team_id: Optional[str]
|
||||
user_api_key_project_id: Optional[str]
|
||||
user_api_key_project_alias: Optional[str]
|
||||
user_api_key_org_id: Optional[str]
|
||||
user_api_key_user_id: Optional[str]
|
||||
user_api_key_team_alias: Optional[str]
|
||||
|
|
|
|||
|
|
@ -836,6 +836,7 @@ async def _user_api_key_auth_builder( # noqa: PLR0915
|
|||
)
|
||||
if _jwt_project_obj is not None:
|
||||
valid_token.project_metadata = _jwt_project_obj.metadata
|
||||
valid_token.project_alias = _jwt_project_obj.project_alias
|
||||
|
||||
# run through common checks
|
||||
_ = await common_checks(
|
||||
|
|
@ -1431,6 +1432,7 @@ async def _user_api_key_auth_builder( # noqa: PLR0915
|
|||
)
|
||||
if _project_obj is not None:
|
||||
valid_token.project_metadata = _project_obj.metadata
|
||||
valid_token.project_alias = _project_obj.project_alias
|
||||
|
||||
global_proxy_spend = None
|
||||
if (
|
||||
|
|
@ -1888,6 +1890,7 @@ async def _run_post_custom_auth_checks(
|
|||
)
|
||||
if _project_obj is not None:
|
||||
valid_token.project_metadata = _project_obj.metadata
|
||||
valid_token.project_alias = _project_obj.project_alias
|
||||
|
||||
if general_settings.get("custom_auth_run_common_checks", False):
|
||||
_ = await common_checks(
|
||||
|
|
|
|||
|
|
@ -27,14 +27,16 @@ async def create_missing_views(db: _db): # noqa: PLR0915
|
|||
await db.execute_raw(
|
||||
"""
|
||||
CREATE VIEW "LiteLLM_VerificationTokenView" AS
|
||||
SELECT
|
||||
v.*,
|
||||
t.spend AS team_spend,
|
||||
t.max_budget AS team_max_budget,
|
||||
t.tpm_limit AS team_tpm_limit,
|
||||
t.rpm_limit AS team_rpm_limit
|
||||
SELECT
|
||||
v.*,
|
||||
t.spend AS team_spend,
|
||||
t.max_budget AS team_max_budget,
|
||||
t.tpm_limit AS team_tpm_limit,
|
||||
t.rpm_limit AS team_rpm_limit,
|
||||
p.project_alias AS project_alias
|
||||
FROM "LiteLLM_VerificationToken" v
|
||||
LEFT JOIN "LiteLLM_TeamTable" t ON v.team_id = t.team_id;
|
||||
LEFT JOIN "LiteLLM_TeamTable" t ON v.team_id = t.team_id
|
||||
LEFT JOIN "LiteLLM_ProjectTable" p ON v.project_id = p.project_id;
|
||||
"""
|
||||
)
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue