Compare commits

...

2 Commits

2 changed files with 195 additions and 53 deletions

153
.github/workflows/runtime-release.yml vendored Normal file
View File

@ -0,0 +1,153 @@
name: Build LiteLLM Runtime Release
on:
push:
branches: [litellm_internal_staging, main]
paths:
- litellm/**
- pyproject.toml
- uv.lock
- .github/workflows/runtime-release.yml
workflow_dispatch:
permissions:
contents: write
concurrency:
group: litellm-runtime-release-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build ${{ matrix.distro }}-${{ matrix.version }}-${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
distro: [debian, ubuntu]
version: ["11", "12", "13", "22.04", "24.04", "26.04"]
arch: [amd64, arm64]
exclude:
- distro: debian
version: "22.04"
- distro: debian
version: "24.04"
- distro: debian
version: "26.04"
- distro: ubuntu
version: "11"
- distro: ubuntu
version: "12"
- distro: ubuntu
version: "13"
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- name: Build target wheelhouse
env:
TARGET_DISTRO: ${{ matrix.distro }}
TARGET_VERSION: ${{ matrix.version }}
TARGET_ARCH: ${{ matrix.arch }}
LITELLM_VERSION: "1.89.0"
LITELLM_DEBIAN_11_VERSION: "1.74.9"
UV_VERSION: "0.11.21"
PORTABLE_PYTHON_VERSION: "3.13.14"
run: |
set -euo pipefail
root="${PWD}/dist/runtime/litellm-runtime"
mkdir -p "${root}/packages/pip" "${root}/packages/python" \
"${root}/metadata" dist/assets
image="${TARGET_DISTRO}:${TARGET_VERSION}"
docker run --rm --platform "linux/${TARGET_ARCH}" \
-e TARGET_DISTRO -e TARGET_VERSION -e TARGET_ARCH \
-e LITELLM_VERSION -e LITELLM_DEBIAN_11_VERSION \
-e UV_VERSION -e PORTABLE_PYTHON_VERSION \
-v "${PWD}:/src:ro" \
-v "${root}:/out" \
"${image}" bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends \
ca-certificates curl build-essential libpq-dev \
python3 python3-dev python3-pip python3-venv
python_bin=python3
if [ "${TARGET_DISTRO}:${TARGET_VERSION}" = "ubuntu:26.04" ]; then
case "${TARGET_ARCH}" in amd64) uv_arch=x86_64 ;; arm64) uv_arch=aarch64 ;; esac
curl -fsSL \
"https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${uv_arch}-unknown-linux-gnu.tar.gz" \
-o /tmp/uv.tar.gz
tar -xzf /tmp/uv.tar.gz -C /tmp
install -m 0755 "$(find /tmp -type f -path "*/uv-*/uv" -print -quit)" /usr/local/bin/uv
uv python install "${PORTABLE_PYTHON_VERSION}" --install-dir /out/packages/python --no-bin
python_bin="$(find -L /out/packages/python -type f -path "*/bin/python3.13" -perm /111 -print -quit)"
find /out/packages/python -name EXTERNALLY-MANAGED -delete
"${python_bin}" -m ensurepip --upgrade
fi
"${python_bin}" -m venv /tmp/wheel-builder
/tmp/wheel-builder/bin/pip install --upgrade pip setuptools wheel
package_spec="/src[proxy]"
runtime_spec="litellm[proxy]==${LITELLM_VERSION}"
if [ "${TARGET_DISTRO}:${TARGET_VERSION}" = "debian:11" ]; then
package_spec="litellm[proxy]==${LITELLM_DEBIAN_11_VERSION}"
runtime_spec="${package_spec}"
fi
/tmp/wheel-builder/bin/pip wheel --wheel-dir /out/packages/pip \
"${package_spec}" prisma psycopg2-binary
printf "LITELLM_PACKAGE_SPEC=%s\n" "${runtime_spec}" > /out/metadata/runtime.env
'
cat > "${root}/manifest.json" <<JSON
{
"component": "litellm",
"commit": "${GITHUB_SHA}",
"distro": "${TARGET_DISTRO}",
"version": "${TARGET_VERSION}",
"arch": "${TARGET_ARCH}"
}
JSON
asset="litellm-runtime-${TARGET_DISTRO}-${TARGET_VERSION}-${TARGET_ARCH}.tar.gz"
tar -czf "dist/assets/${asset}" -C dist/runtime litellm-runtime
(
cd dist/assets
sha256sum -- "./${asset}" | sed 's# \./# #' > \
"SHA256SUMS-${TARGET_DISTRO}-${TARGET_VERSION}-${TARGET_ARCH}"
)
- uses: actions/upload-artifact@v4
with:
name: litellm-runtime-${{ matrix.distro }}-${{ matrix.version }}-${{ matrix.arch }}
path: |
dist/assets/*.tar.gz
dist/assets/SHA256SUMS-*
if-no-files-found: error
publish:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
pattern: litellm-runtime-*
path: dist
merge-multiple: true
- name: Publish runtime release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="runtime-${GITHUB_SHA::12}"
cat dist/SHA256SUMS-* | sort -u > dist/SHA256SUMS
rm -f dist/SHA256SUMS-*
if gh release view "${tag}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
gh release upload "${tag}" dist/*.tar.gz dist/SHA256SUMS \
--repo "${GITHUB_REPOSITORY}" --clobber
else
gh release create "${tag}" dist/*.tar.gz dist/SHA256SUMS \
--repo "${GITHUB_REPOSITORY}" \
--target "${GITHUB_SHA}" \
--title "LiteLLM runtime ${GITHUB_SHA::12}" \
--notes "Prebuilt wheelhouses for maintained Debian and Ubuntu targets."
fi

View File

@ -840,7 +840,46 @@ async def google_login(
prisma_client,
user_api_key_cache,
user_custom_ui_sso_sign_in_handler,
master_key,
)
import secrets
import jwt
from fastapi.responses import RedirectResponse
from litellm.proxy.auth.login_utils import authenticate_user, create_ui_token_object
from litellm.proxy.utils import get_custom_url
if key is not None and master_key is not None:
try:
if secrets.compare_digest(key.encode("utf-8"), master_key.encode("utf-8")):
ui_username = os.getenv("UI_USERNAME", "admin")
login_result = await authenticate_user(
username=ui_username,
password=key,
master_key=master_key,
prisma_client=prisma_client,
)
from litellm.proxy.proxy_server import general_settings
returned_ui_token_object = create_ui_token_object(
login_result=login_result,
general_settings=general_settings,
premium_user=False,
)
jwt_token = jwt.encode(
dict(returned_ui_token_object),
master_key,
algorithm="HS256",
)
litellm_dashboard_ui = get_custom_url(str(request.base_url))
if litellm_dashboard_ui.endswith("/"):
litellm_dashboard_ui += "ui/"
else:
litellm_dashboard_ui += "/ui/"
litellm_dashboard_ui += "?login=success"
response_redirect = RedirectResponse(url=litellm_dashboard_ui, status_code=307)
response_redirect.set_cookie(key="token", value=jwt_token, path="/")
return response_redirect
except Exception as e:
verbose_proxy_logger.error(f"Unified Auth Token SSO login failed: {e}")
microsoft_client_id = os.getenv("MICROSOFT_CLIENT_ID", None)
google_client_id = os.getenv("GOOGLE_CLIENT_ID", None)
@ -853,31 +892,6 @@ async def google_login(
if is_disabled:
return admin_ui_disabled()
####### Check if user is a Enterprise / Premium User #######
if (
microsoft_client_id is not None
or google_client_id is not None
or generic_client_id is not None
):
if premium_user is not True:
# Check if under 'free SSO user' limit
if prisma_client is not None:
total_users = await UserRepository(prisma_client).table.count()
if total_users and total_users > 5:
raise ProxyException(
message="You must be a LiteLLM Enterprise user to use SSO for more than 5 users. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://enterprise.litellm.ai/demo You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
type=ProxyErrorTypes.auth_error,
param="premium_user",
code=status.HTTP_403_FORBIDDEN,
)
else:
raise ProxyException(
message=CommonProxyErrors.db_not_connected_error.value,
type=ProxyErrorTypes.auth_error,
param="premium_user",
code=status.HTTP_403_FORBIDDEN,
)
####### Detect DB + MASTER KEY in .env #######
missing_env_vars = show_missing_vars_in_env()
if missing_env_vars is not None:
@ -901,18 +915,9 @@ async def google_login(
# check if user defined a custom auth sso sign in handler, if yes, use it
if user_custom_ui_sso_sign_in_handler is not None:
try:
from litellm_enterprise.proxy.auth.custom_sso_handler import ( # type: ignore[import-untyped]
EnterpriseCustomSSOHandler,
)
return await EnterpriseCustomSSOHandler.handle_custom_ui_sso_sign_in(
request=request,
)
except ImportError:
raise ValueError(
"Enterprise features are not available. Custom UI SSO sign-in requires LiteLLM Enterprise."
)
raise ValueError(
"Custom UI SSO sign-in handler is not available in this MIT-only build."
)
# Check if we should use SSO handler
if (
@ -4350,26 +4355,10 @@ async def debug_sso_login(request: Request):
PROXY_BASE_URL should be the your deployed proxy endpoint, e.g. PROXY_BASE_URL="https://litellm-production-7002.up.railway.app/"
Example:
"""
from litellm.proxy.proxy_server import premium_user
microsoft_client_id = os.getenv("MICROSOFT_CLIENT_ID", None)
google_client_id = os.getenv("GOOGLE_CLIENT_ID", None)
generic_client_id = os.getenv("GENERIC_CLIENT_ID", None)
####### Check if user is a Enterprise / Premium User #######
if (
microsoft_client_id is not None
or google_client_id is not None
or generic_client_id is not None
):
if premium_user is not True:
raise ProxyException(
message="You must be a LiteLLM Enterprise user to use SSO. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://enterprise.litellm.ai/demo You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
type=ProxyErrorTypes.auth_error,
param="premium_user",
code=status.HTTP_403_FORBIDDEN,
)
# get url from request
redirect_url = SSOAuthenticationHandler.get_redirect_url_for_sso(
request=request,