chore: move workspace packages under litellm (#51000)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Yassin Kortam 2026-11-11 20:19:57 -08:00 • committed by GitHub
parent f6bee81c73
commit db6dfb6bb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2043 changed files with 763 additions and 271 deletions

View file

@ -26,7 +26,7 @@ metadata:
- Keep query execution inside the repository, with one implementation per repository and no backend switch or fallback
- A repository is either all Prisma or all native, never a mix of both
- Prisma is fine for the implementation, adding a query does not require a Rust port
- Native Rust callers use `litellm-rust/crates/db` directly
- Native Rust callers use `litellm/rust/crates/db` directly
## Framework wiring

View file

@ -81,11 +81,11 @@ Use this only when a port is explicitly requested. Repository extraction does no
- Identify the repository's inputs, outputs, errors and transaction boundary
- List repositories that share a transaction with it, they move together
- Check `litellm-rust/crates/db` for an equivalent query and reuse it when its behavior matches
- Check `litellm/rust/crates/db` for an equivalent query and reuse it when its behavior matches
### Steps
1. Rust queries, following `litellm-rust/crates/db/AGENTS.md`
1. Rust queries, following `litellm/rust/crates/db/AGENTS.md`
- Static SQL in `queries/`, compile-checked with SQLx, typed inputs and row projections in `src/`
- Dynamic filters and sort keys become closed Rust enums that select static SQL, every value is bound
- Refresh SQLx query metadata after SQL changes

View file

@ -3,7 +3,7 @@ description: >-
Cache the Cargo registry and target directory the root package's build needs,
so only the first job on a given Cargo.lock compiles the bridge from scratch.
litellm builds through maturin, which compiles litellm-rust/crates/python-bridge
litellm builds through maturin, which compiles litellm/rust/crates/python-bridge
in the dev profile for editable installs. `uv sync` therefore pays a full build
in every job that installs the workspace. Nothing caught it, because the uv cache
holds wheels uv downloads rather than wheels it builds, and a path dependency
@ -31,8 +31,8 @@ runs:
path: |
~/.cargo/registry
~/.cargo/git
litellm-rust/target
key: ${{ runner.os }}-maturin-${{ inputs.profile }}-${{ hashFiles('litellm-rust/Cargo.lock') }}
litellm/rust/target
key: ${{ runner.os }}-maturin-${{ inputs.profile }}-${{ hashFiles('litellm/rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-maturin-${{ inputs.profile }}-
@ -43,7 +43,7 @@ runs:
path: |
~/.cargo/registry
~/.cargo/git
litellm-rust/target
key: ${{ runner.os }}-maturin-${{ inputs.profile }}-${{ hashFiles('litellm-rust/Cargo.lock') }}
litellm/rust/target
key: ${{ runner.os }}-maturin-${{ inputs.profile }}-${{ hashFiles('litellm/rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-maturin-${{ inputs.profile }}-

View file

@ -73,4 +73,4 @@ dockerfiles:
clippy, and compile jobs already cover the code it packages. Revisit when the gateway is
published
paths:
- litellm-rust/crates/gateway/Dockerfile
- litellm/rust/crates/gateway/Dockerfile

4
.github/codecov.yml vendored
View file

@ -4,7 +4,7 @@ codecov:
wait_for_ci: false # post as soon as expected uploads arrive, don't wait on CI
ignore:
- "litellm-rust/**"
- "litellm/rust/**"
# Flagged uploads avoid overlapping sessions on reruns.
# Only the flags below reuse parent coverage, avoiding stale line maps elsewhere.
@ -40,7 +40,7 @@ component_management:
- "*/proxy/auth/**"
- component_id: "Enterprise"
paths:
- "enterprise/**"
- "litellm/enterprise/**"
- component_id: "Batches"
paths:
- "*/proxy/batches_endpoints/**"

49
.github/packaging/build_backend.py vendored Normal file
View file

@ -0,0 +1,49 @@
import sys
from collections.abc import Mapping
from pathlib import Path
from typing import Final
import maturin
from cli_package import PACKAGE_DIR, map_wheel
from maturin import (
build_sdist as build_sdist,
)
from maturin import (
get_requires_for_build_editable as get_requires_for_build_editable,
)
from maturin import (
get_requires_for_build_sdist as get_requires_for_build_sdist,
)
from maturin import (
get_requires_for_build_wheel as get_requires_for_build_wheel,
)
from maturin import (
prepare_metadata_for_build_editable as prepare_metadata_for_build_editable,
)
from maturin import (
prepare_metadata_for_build_wheel as prepare_metadata_for_build_wheel,
)
def build_wheel(
wheel_directory: str,
config_settings: Mapping[str, str | list[str]] | None = None,
metadata_directory: str | None = None,
) -> str:
filename: Final = maturin.build_wheel(wheel_directory, config_settings, metadata_directory)
map_wheel(Path(wheel_directory) / filename)
return filename
def build_editable(
wheel_directory: str,
config_settings: Mapping[str, str | list[str]] | None = None,
metadata_directory: str | None = None,
) -> str:
filename: Final = maturin.build_editable(wheel_directory, config_settings, metadata_directory)
map_wheel(Path(wheel_directory) / filename, source=Path(__file__).resolve().parents[2] / PACKAGE_DIR)
return filename
if __name__ == "__main__":
build_editable(sys.argv[1])

93
.github/packaging/cli_package.py vendored Normal file
View file

@ -0,0 +1,93 @@
import argparse
import base64
import copy
import csv
import hashlib
import io
import os
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Final
from zipfile import ZipFile, ZipInfo
PACKAGE_DIR: Final = "litellm/cli"
PACKAGE_NAME: Final = "litellm_cli"
def editable_support(source: Path) -> tuple[tuple[str, bytes], ...]:
module: Final = "_litellm_cli_editable"
finder: Final = (
"import importlib.util\nimport sys\n\n"
"class _Finder:\n"
" @staticmethod\n"
" def find_spec(fullname, path=None, target=None):\n"
f" if fullname == {PACKAGE_NAME!r}:\n"
" return importlib.util.spec_from_file_location(\n"
f" fullname, {str(source / '__init__.py')!r},\n"
f" submodule_search_locations=[{str(source)!r}])\n"
" return None\n\n"
"sys.meta_path.insert(0, _Finder())\n"
)
return ((f"{module}.py", finder.encode()), (f"{module}.pth", f"import {module}\n".encode()))
def _name(name: str) -> str:
prefix: Final = f"{PACKAGE_DIR}/"
return f"{PACKAGE_NAME}/{name.removeprefix(prefix)}" if name.startswith(prefix) else name
def _info(original: ZipInfo) -> ZipInfo:
mapped: Final = copy.copy(original)
mapped.filename = _name(original.filename)
return mapped
def _record(entries: tuple[tuple[ZipInfo, bytes], ...], record: str) -> bytes:
buffer: Final = io.StringIO(newline="")
writer: Final = csv.writer(buffer, lineterminator="\n")
writer.writerows(
(
info.filename,
"sha256=" + base64.urlsafe_b64encode(hashlib.sha256(data).digest()).rstrip(b"=").decode(),
str(len(data)),
)
for info, data in entries
)
writer.writerow((record, "", ""))
return buffer.getvalue().encode()
def map_wheel(wheel: Path, *, source: Path | None = None) -> None:
with ZipFile(wheel) as archive:
records: Final = tuple(info for info in archive.infolist() if info.filename.endswith(".dist-info/RECORD"))
if len(records) != 1:
raise ValueError("A wheel must contain exactly one RECORD")
entries: Final = tuple(
(_info(info), archive.read(info))
for info in archive.infolist()
if info != records[0] and (source is None or not info.filename.startswith(f"{PACKAGE_DIR}/"))
)
additions: Final = () if source is None else tuple((ZipInfo(name), data) for name, data in editable_support(source))
contents: Final = (*entries, *additions)
names: Final = tuple(info.filename for info, _ in contents)
if len(names) != len(frozenset(names)):
raise ValueError("The CLI package mapping would overwrite a wheel member")
if source is None and not any(name.startswith(f"{PACKAGE_NAME}/") for name in names):
raise ValueError("The wheel does not contain the CLI package")
with NamedTemporaryFile(dir=wheel.parent, suffix=".whl", delete=False) as temporary:
destination: Final = Path(temporary.name)
try:
with ZipFile(destination, "w") as archive:
for info, data in contents:
archive.writestr(info, data)
archive.writestr(records[0], _record(contents, records[0].filename))
os.replace(destination, wheel)
finally:
destination.unlink(missing_ok=True)
if __name__ == "__main__":
parser: Final = argparse.ArgumentParser()
parser.add_argument("wheels", nargs="+", type=Path)
for wheel in parser.parse_args().wheels:
map_wheel(wheel)

View file

@ -19,11 +19,11 @@ while IFS= read -r file || [ -n "$file" ]; do
esac
case "$file" in
*.md | *.mdx) : ;;
pyproject.toml | */pyproject.toml | uv.lock | uv.toml | .python-version | rust-toolchain.toml | litellm-rust/* | litellm/__init__.py | litellm/proxy/proxy_server.py | litellm/*mcp* | tests/*mcp* | litellm/integrations/arize/* | tests/base_sdk_tests/* | scripts/check_mcp_sdk_install.py | .github/workflows/test-mcp-dependency-resolution.yml | .github/actions/detect-changes/* | .github/actions/setup-uv-with-retries/* | .github/actions/cache-cargo-build/* | .github/scripts/detect_changes.sh | .github/scripts/uv_sync_with_retries.sh | .github/scripts/ci/classify_changes.sh | tests/unit/test_ci_path_filter.py | tests/unit/test_detect_changes.py)
pyproject.toml | */pyproject.toml | uv.lock | uv.toml | .python-version | rust-toolchain.toml | litellm/rust/* | litellm/__init__.py | litellm/proxy/proxy_server.py | litellm/*mcp* | tests/*mcp* | litellm/integrations/arize/* | tests/base_sdk_tests/* | scripts/check_mcp_sdk_install.py | .github/workflows/test-mcp-dependency-resolution.yml | .github/actions/detect-changes/* | .github/actions/setup-uv-with-retries/* | .github/actions/cache-cargo-build/* | .github/scripts/detect_changes.sh | .github/scripts/uv_sync_with_retries.sh | .github/scripts/ci/classify_changes.sh | tests/unit/test_ci_path_filter.py | tests/unit/test_detect_changes.py)
has_mcp_dependencies=true ;;
esac
case "$file" in
litellm-rust/* | litellm/rust_bridge/* | rust-toolchain.toml | pyproject.toml | uv.lock | tests/windows_tests/* | .github/scripts/ci/* | .github/actions/*)
litellm/rust/* | litellm/rust_bridge/* | rust-toolchain.toml | pyproject.toml | uv.lock | tests/windows_tests/* | .github/scripts/ci/* | .github/actions/*)
has_windows_release=true ;;
esac
case "$file" in

View file

@ -128,7 +128,17 @@ def main(
member.filename
for member in wheel_members
if member.filename.endswith((".pdb", ".dwp", ".rlib", ".rmeta", "Cargo.toml", "Cargo.lock"))
or member.filename.startswith(("litellm/deploy/", "litellm/ui/", "litellm/vscode-extension/"))
or member.filename.startswith(
(
"litellm/deploy/",
"litellm/ui/",
"litellm/vscode-extension/",
"litellm/enterprise/",
"litellm/proxy-extras/",
"litellm/rust/",
"litellm/packaging/",
)
)
or any(part.endswith(".dSYM") for part in PurePosixPath(member.filename).parts)
)
native_member: Final = native_members[0]

View file

@ -5,7 +5,7 @@ on:
paths:
- 'schema.prisma'
- 'litellm/proxy/schema.prisma'
- 'litellm-proxy-extras/litellm_proxy_extras/schema.prisma'
- 'litellm/proxy-extras/litellm_proxy_extras/schema.prisma'
permissions:
contents: read
@ -27,7 +27,7 @@ jobs:
- name: Reject symlinked schema files
run: |
for f in schema.prisma litellm/proxy/schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma; do
for f in schema.prisma litellm/proxy/schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma; do
if [ -L "$f" ]; then
echo "::error file=$f::$f is a symlink, which is not allowed"
exit 1
@ -43,8 +43,8 @@ jobs:
EXIT=1
}
diff schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma || {
echo "::error file=litellm-proxy-extras/litellm_proxy_extras/schema.prisma::litellm-proxy-extras/litellm_proxy_extras/schema.prisma differs from root schema.prisma"
diff schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma || {
echo "::error file=litellm/proxy-extras/litellm_proxy_extras/schema.prisma::litellm/proxy-extras/litellm_proxy_extras/schema.prisma differs from root schema.prisma"
EXIT=1
}
@ -55,7 +55,7 @@ jobs:
echo ""
echo "To fix, run from the repo root:"
echo " cp schema.prisma litellm/proxy/schema.prisma"
echo " cp schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma"
echo " cp schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma"
exit 1
fi

View file

@ -74,5 +74,5 @@ jobs:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
git show "${HEAD_SHA}:model_prices_and_context_window.json" > "$RUNNER_TEMP/model_prices_and_context_window.json"
cargo run --locked --quiet --manifest-path litellm-rust/Cargo.toml -p litellm-model-catalog --bin check-registry \
cargo run --locked --quiet --manifest-path litellm/rust/Cargo.toml -p litellm-model-catalog --bin check-registry \
-- "$RUNNER_TEMP/model_prices_and_context_window.json"

View file

@ -8,8 +8,8 @@ on:
paths:
- "uv.lock"
- "pyproject.toml"
- "litellm-proxy-extras/pyproject.toml"
- "enterprise/pyproject.toml"
- "litellm/proxy-extras/pyproject.toml"
- "litellm/enterprise/pyproject.toml"
permissions: {}
@ -130,8 +130,12 @@ jobs:
}
check_deps base/pyproject.toml pr/pyproject.toml "pyproject.toml" || had_error=1
check_deps base/litellm-proxy-extras/pyproject.toml pr/litellm-proxy-extras/pyproject.toml "litellm-proxy-extras/pyproject.toml" || had_error=1
check_deps base/enterprise/pyproject.toml pr/enterprise/pyproject.toml "enterprise/pyproject.toml" || had_error=1
base_extras=base/litellm/proxy-extras/pyproject.toml
[ -f "$base_extras" ] || base_extras=base/litellm-proxy-extras/pyproject.toml
base_enterprise=base/litellm/enterprise/pyproject.toml
[ -f "$base_enterprise" ] || base_enterprise=base/enterprise/pyproject.toml
check_deps "$base_extras" pr/litellm/proxy-extras/pyproject.toml "litellm/proxy-extras/pyproject.toml" || had_error=1
check_deps "$base_enterprise" pr/litellm/enterprise/pyproject.toml "litellm/enterprise/pyproject.toml" || had_error=1
if [ "$had_error" -ne 0 ]; then
echo ""

View file

@ -14,16 +14,16 @@ on:
- gateway/main.py
- gateway/launch.py
- gateway/routes/allowlist.py
- litellm_cli/**
- litellm/cli/**
- backend/main.py
- backend/routes/allowlist.py
- litellm-rust/**
- litellm/rust/**
- litellm/proxy/lens/**
- litellm/proxy/prisma_migration.py
- litellm/proxy/admin_mcp.py
- litellm/proxy/proxy_server.py
- pyproject.toml
- litellm-proxy-extras/**
- litellm/proxy-extras/**
- tests/integration/database/image/**
- uv.lock
- litellm/ui/litellm-dashboard/package-lock.json

View file

@ -37,7 +37,7 @@ jobs:
- name: Reject symlinked schema files
run: |
for f in schema.prisma litellm/proxy/schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma; do
for f in schema.prisma litellm/proxy/schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma; do
if [ -L "$f" ]; then
echo "::error file=$f::$f is a symlink, which is not allowed"
exit 1
@ -47,12 +47,12 @@ jobs:
- name: Copy root schema to other locations
run: |
cp schema.prisma litellm/proxy/schema.prisma
cp schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma
cp schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma
- name: Check for changes
id: diff
run: |
if git diff --quiet -- litellm/proxy/schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma; then
if git diff --quiet -- litellm/proxy/schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma; then
echo "changed=false" >> "$GITHUB_OUTPUT"
echo "Schemas already in sync. Nothing to do."
else
@ -68,6 +68,6 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B "$GITHUB_HEAD_REF"
git add -- litellm/proxy/schema.prisma litellm-proxy-extras/litellm_proxy_extras/schema.prisma
git add -- litellm/proxy/schema.prisma litellm/proxy-extras/litellm_proxy_extras/schema.prisma
git commit -m "chore: sync schema.prisma copies from root"
git push origin "HEAD:$GITHUB_HEAD_REF"

View file

@ -114,7 +114,7 @@ jobs:
- name: Check ruff format
if: steps.changes.outputs.decision != 'skip'
run: |
git diff --name-only --diff-filter=ACMR "$GATE_BASE_SHA" HEAD -- ':(glob)litellm/**/*.py' | grep -Ev '^litellm/(enterprise|ui|vscode-extension)/' > "$RUNNER_TEMP/ruff_format_files.txt" || true
git diff --name-only --diff-filter=ACMR "$GATE_BASE_SHA" HEAD -- ':(glob)litellm/**/*.py' | grep -Ev '^litellm/(enterprise|ui|vscode-extension|rust|proxy-extras|packaging|cli)/' > "$RUNNER_TEMP/ruff_format_files.txt" || true
if [ ! -s "$RUNNER_TEMP/ruff_format_files.txt" ]; then
echo "No changed litellm Python files to check with ruff format."
exit 0
@ -375,7 +375,7 @@ jobs:
exit 0
fi
files="$(git diff --name-only "$base" HEAD)"
if grep -Ev '^litellm/(ui|vscode-extension)/' <<< "$files" | grep -E '^(litellm/|enterprise/|(pyproject\.toml|uv\.lock)$|\.github/workflows/test-linting\.yml$)' >/dev/null || grep -Eq '^litellm/ui/litellm-dashboard/(src/lib/http/schema\.d\.ts|scripts/gen-api-types\.mjs|package(-lock)?\.json)$' <<< "$files"; then
if grep -Ev '^litellm/(ui|vscode-extension|rust|proxy-extras|packaging|cli)/' <<< "$files" | grep -E '^(litellm/|enterprise/|(pyproject\.toml|uv\.lock)$|\.github/workflows/test-linting\.yml$)' >/dev/null || grep -Eq '^litellm/ui/litellm-dashboard/(src/lib/http/schema\.d\.ts|scripts/gen-api-types\.mjs|package(-lock)?\.json)$' <<< "$files"; then
echo "relevant=true" >> "$GITHUB_OUTPUT"
else
echo "No litellm, enterprise, dependency or generator changes in this pull request, nothing to verify."

View file

@ -11,7 +11,7 @@ on:
- "uv.toml"
- ".python-version"
- "rust-toolchain.toml"
- "litellm-rust/**"
- "litellm/rust/**"
- "litellm/__init__.py"
- "litellm/_version.py"
- "litellm/_lazy_imports_registry.py"

View file

@ -3,11 +3,11 @@ name: LiteLLM Rust DB
on:
push:
paths:
- "litellm-rust/crates/db/**"
- "litellm-rust/Cargo.toml"
- "litellm-rust/Cargo.lock"
- "litellm-rust/clippy.toml"
- "litellm-proxy-extras/litellm_proxy_extras/migrations/**"
- "litellm/rust/crates/db/**"
- "litellm/rust/Cargo.toml"
- "litellm/rust/Cargo.lock"
- "litellm/rust/clippy.toml"
- "litellm/proxy-extras/litellm_proxy_extras/migrations/**"
- "**/schema.prisma"
- ".cargo/**"
- "rust-toolchain.toml"
@ -20,11 +20,11 @@ on:
- main
- "litellm_**"
paths:
- "litellm-rust/crates/db/**"
- "litellm-rust/Cargo.toml"
- "litellm-rust/Cargo.lock"
- "litellm-rust/clippy.toml"
- "litellm-proxy-extras/litellm_proxy_extras/migrations/**"
- "litellm/rust/crates/db/**"
- "litellm/rust/Cargo.toml"
- "litellm/rust/Cargo.lock"
- "litellm/rust/clippy.toml"
- "litellm/proxy-extras/litellm_proxy_extras/migrations/**"
- "**/schema.prisma"
- ".cargo/**"
- "rust-toolchain.toml"
@ -71,7 +71,7 @@ jobs:
SQLX_OFFLINE: "false"
defaults:
run:
working-directory: litellm-rust
working-directory: litellm/rust
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
@ -89,7 +89,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: litellm-rust
workspaces: litellm/rust
cache-on-failure: true
- name: Set up sccache
@ -103,13 +103,13 @@ jobs:
- name: Deploy Prisma migrations
working-directory: .
run: uv run --no-sync prisma migrate deploy --schema litellm-proxy-extras/litellm_proxy_extras/schema.prisma
run: uv run --no-sync prisma migrate deploy --schema litellm/proxy-extras/litellm_proxy_extras/schema.prisma
- name: Install SQLx CLI
run: cargo install sqlx-cli --version 0.9.0 --locked --no-default-features -F postgres
- name: Check SQLx query metadata
working-directory: litellm-rust/crates/db
working-directory: litellm/rust/crates/db
run: cargo sqlx prepare --check
- name: Test database queries

View file

@ -3,7 +3,7 @@ name: LiteLLM Rust
on:
push:
paths:
- "litellm-rust/**"
- "litellm/rust/**"
- "model_prices_and_context_window.json"
- "litellm/rust_bridge/**"
- "scripts/generate_trace_types.py"
@ -38,7 +38,7 @@ on:
- main
- "litellm_**"
paths:
- "litellm-rust/**"
- "litellm/rust/**"
- "model_prices_and_context_window.json"
- "litellm/rust_bridge/**"
- "scripts/generate_trace_types.py"
@ -90,7 +90,7 @@ jobs:
timeout-minutes: 15
defaults:
run:
working-directory: litellm-rust
working-directory: litellm/rust
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
@ -102,7 +102,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: litellm-rust
workspaces: litellm/rust
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}
@ -121,7 +121,7 @@ jobs:
CARGO_PROFILE_DEV_DEBUG: line-tables-only
defaults:
run:
working-directory: litellm-rust
working-directory: litellm/rust
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
@ -149,7 +149,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: litellm-rust
workspaces: litellm/rust
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}
@ -200,7 +200,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: litellm-rust
workspaces: litellm/rust
cache-on-failure: true
save-if: ${{ github.ref == 'refs/heads/main' }}

View file

@ -12,7 +12,7 @@ on:
paths:
- "litellm/deploy/terraform/provider/**"
- "litellm/proxy/**"
- "enterprise/**"
- "litellm/enterprise/**"
- ".github/workflows/test-terraform-provider.yml"
permissions:

View file

@ -45,7 +45,7 @@ jobs:
id: rust-bridge-key
if: steps.changes.outputs.decision != 'skip'
env:
RUST_BRIDGE_CACHE_KEY: ${{ runner.os }}-rust-bridge-editable-${{ hashFiles('litellm-rust/Cargo.lock', 'litellm-rust/Cargo.toml', 'litellm-rust/crates/**', 'model_prices_and_context_window.json', 'pyproject.toml', 'packaging/litellm-proxy/pyproject.toml') }}
RUST_BRIDGE_CACHE_KEY: ${{ runner.os }}-rust-bridge-editable-${{ hashFiles('litellm/rust/Cargo.lock', 'litellm/rust/Cargo.toml', 'litellm/rust/crates/**', 'model_prices_and_context_window.json', 'pyproject.toml', 'litellm/packaging/litellm-proxy/pyproject.toml') }}
run: echo "key=$RUST_BRIDGE_CACHE_KEY" >> "$GITHUB_OUTPUT"
- name: Set up Python
@ -93,7 +93,7 @@ jobs:
CARGO_INCREMENTAL: "0"
run: |
mkdir -p rust-bridge-dist
uv run --no-project --with maturin==1.15.0 python -c 'import maturin; maturin.build_editable("rust-bridge-dist")'
uv run --no-project --with maturin==1.15.0 python .github/packaging/build_backend.py rust-bridge-dist
strip --strip-debug litellm/rust_bridge/_native.abi3.so
mkdir -p rust-bridge-dist/litellm/rust_bridge
cp litellm/rust_bridge/_native.abi3.so rust-bridge-dist/litellm/rust_bridge/_native.abi3.so
@ -101,14 +101,14 @@ jobs:
- name: Build the gateway wheel once for test shards
if: steps.changes.outputs.decision != 'skip' && steps.rust-bridge-cache.outputs.cache-hit != 'true'
timeout-minutes: 15
working-directory: packaging/litellm-proxy
working-directory: litellm/packaging/litellm-proxy
env:
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
SCCACHE_GHA_VERSION: litellm-ci-v1
SCCACHE_GHA_RW_MODE: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'READ_WRITE' || 'READ_ONLY' }}
CARGO_INCREMENTAL: "0"
run: uv run --no-project --with maturin==1.15.0 maturin build --profile dev --strip --out ../../rust-bridge-dist
run: uv run --no-project --with maturin==1.15.0 maturin build --profile dev --strip --out ../../../rust-bridge-dist
- name: Upload Rust bridge artifact
if: steps.changes.outputs.decision != 'skip'
@ -1158,7 +1158,7 @@ jobs:
--timeout="${TEST_TIMEOUT_SECONDS}" \
--rerun-except "from pytest-timeout" \
--durations=20 \
--cov=./litellm --cov=./enterprise/litellm_enterprise \
--cov=./litellm --cov=./litellm/enterprise/litellm_enterprise \
--cov-report=xml:coverage.xml \
--cov-config=pyproject.toml
status=$?

2
.gitignore vendored
View file

@ -18,7 +18,7 @@ __pycache__/
# Rust bridge build artifacts (compiled, platform-specific; regenerated by maturin/cargo)
litellm/rust_bridge/_native*.so
litellm/rust_bridge/_native*.pyd
litellm-rust/target/
litellm/rust/target/
# Python package build output
dist/

View file

@ -1,6 +1,6 @@
Portions of this software are licensed as follows:
* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE".
* All content that resides under the "litellm/enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "litellm/enterprise/LICENSE".
* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below.
---
MIT License

View file

@ -54,7 +54,7 @@ help:
@echo " make test-integration - Run integration tests"
@echo " make test-unit-helm - Run helm unit tests"
@echo " make test-rust-extension - Build the Rust extension and run its public Python tests"
@echo " make rust-sqlx-prepare - Refresh litellm-rust/crates/db/.sqlx against a migrated Postgres container"
@echo " make rust-sqlx-prepare - Refresh litellm/rust/crates/db/.sqlx against a migrated Postgres container"
@echo ""
@echo "Heavy targets (check, lint) queue for LITELLM_GATE_SLOTS machine-wide"
@echo "slots (default 2; 0 disables) so parallel sessions don't thrash one machine."
@ -294,7 +294,7 @@ test-rust-extension:
"$$temporary/venv/bin/python" -I -m pytest --import-mode=importlib -m requires_rust_extension tests/test_litellm_rust
rust-sqlx-prepare:
cd litellm-rust && cargo run -p litellm-db-testing --bin sqlx-prepare
cd litellm/rust && cargo run -p litellm-db-testing --bin sqlx-prepare
test: install-test-deps
$(UV_RUN) pytest tests/

View file

@ -25,8 +25,6 @@ from typing import TYPE_CHECKING, Final
from typing_extensions import assert_never
from uvicorn.main import main as uvicorn_main
from litellm_cli.main import without_gc_pauses
if TYPE_CHECKING:
from litellm.proxy.db.db_url_settings import DatabaseURLSettings
from litellm.proxy.db.pgbouncer import PgBouncerError, PgBouncerSettings
@ -67,6 +65,8 @@ def pool_database_url(
def worker_app() -> "ASGIApp":
from litellm_cli.main import without_gc_pauses
with without_gc_pauses():
from gateway.main import app
from litellm.proxy.rust_forward.mode import rust_forward_mode

View file

@ -689,7 +689,7 @@ class LevelRoutingStreamHandler(logging.StreamHandler):
def _parse_env_flag(value: str | None) -> bool:
"""Strict opt-in parse for a boolean env var such as JSON_LOGS: only "true" (any case) enables it.
Matches the reader in litellm-proxy-extras/_logging.py. The previous
Matches the reader in litellm/proxy-extras/_logging.py. The previous
bool(os.getenv(...)) treated any non-empty value, including "false" and "0",
as enabled.
"""

View file

@ -74,9 +74,9 @@ ENV UV_PROJECT_ENVIRONMENT=/app/.venv \
# Copy dependency metadata first for layer caching
COPY pyproject.toml uv.lock ./
COPY enterprise/pyproject.toml enterprise/
COPY litellm-proxy-extras/pyproject.toml litellm-proxy-extras/
COPY packaging/litellm-proxy/pyproject.toml packaging/litellm-proxy/
COPY litellm/enterprise/pyproject.toml litellm/enterprise/
COPY litellm/proxy-extras/pyproject.toml litellm/proxy-extras/
COPY litellm/packaging/litellm-proxy/pyproject.toml litellm/packaging/litellm-proxy/
# Install third-party dependencies (cached unless pyproject.toml/uv.lock change)
RUN uv sync --frozen --no-install-project --no-install-workspace --no-default-groups --no-editable \
@ -116,6 +116,8 @@ RUN uv sync --frozen --no-install-package litellm-proxy --no-default-groups --no
RUN uv pip install --python /app/.venv/bin/python --no-deps /wheels/litellm_proxy-*.whl
RUN cp -r litellm/enterprise "$(.venv/bin/python -c 'import sysconfig; print(sysconfig.get_path("purelib"))')/litellm/enterprise"
RUN HOME=/opt/prisma XDG_CACHE_HOME=/opt/prisma/.cache PRISMA_BINARY_CACHE_DIR=/opt/prisma/binaries \
npm_config_cache=/root/.npm \
prisma generate --schema=./schema.prisma
@ -183,9 +185,7 @@ COPY --from=builder /app/schema.prisma /app/schema.prisma
COPY --from=builder /app/litellm/proxy/prisma_migration.py /app/litellm/proxy/prisma_migration.py
# enterprise/ is imported by source path at runtime (proxy_cli puts the
# working directory on sys.path; litellm/proxy/hooks resolves
# enterprise.enterprise_hooks from it)
COPY --from=builder /app/enterprise /app/enterprise
COPY --from=builder /app/litellm-proxy-extras /app/litellm-proxy-extras
# litellm.enterprise.enterprise_hooks from it)
COPY --from=builder /app/gateway /app/gateway
COPY --from=builder /app/backend /app/backend
COPY --from=builder /app/migrations /app/migrations

View file

@ -1,6 +1,8 @@
litellm/vscode-extension
litellm/proxy/discovery_endpoints/skill_scanner_adapter
.github
.github/*
!.github/packaging/
!.github/packaging/*.py
tests
.git
.devcontainer
@ -49,7 +51,7 @@ build/
**/node_modules
litellm/ui/litellm-dashboard/.next
litellm/ui/litellm-dashboard/out
litellm-rust/target/
litellm/rust/target/
litellm/rust_bridge/_native*.so
*.log
.env

View file

@ -8,8 +8,8 @@ echo
pwd
# only run this step for litellm enterprise, we run this if enterprise/enterprise_ui/_enterprise.json exists
if [ ! -f "enterprise/enterprise_ui/enterprise_colors.json" ]; then
# only run this step for litellm enterprise, we run this if litellm/enterprise/enterprise_ui/_enterprise.json exists
if [ ! -f "litellm/enterprise/enterprise_ui/enterprise_colors.json" ]; then
echo "Admin UI - using default LiteLLM UI"
exit 0
fi

Some files were not shown because too many files have changed in this diff Show more