chore: relocate gateway sources without changing worker imports

This commit is contained in:
Devin AI 2026-11-10 19:56:17 +00:00
parent 932706dba6
commit 6043307625
15 changed files with 20 additions and 14 deletions

View file

@ -11,9 +11,9 @@ on:
- litellm/deploy/docker/Dockerfile.dockerignore
- litellm/deploy/docker/docker-entrypoint.sh
- litellm/runtime/migrations/run.py
- gateway/main.py
- gateway/launch.py
- gateway/routes/allowlist.py
- litellm/runtime/gateway/main.py
- litellm/runtime/gateway/launch.py
- litellm/runtime/gateway/routes/allowlist.py
- litellm/cli/**
- litellm/runtime/backend/main.py
- litellm/runtime/backend/routes/allowlist.py

View file

@ -181,7 +181,7 @@ COPY --from=builder /app/.venv /app/.venv
COPY --from=liteadmin-builder /opt/liteadmin /opt/liteadmin
COPY --from=builder /app/litellm/deploy/docker/docker-entrypoint.sh /app/docker-entrypoint.sh
COPY --from=builder /app/litellm/schema.prisma /app/litellm/schema.prisma
COPY --from=builder /app/gateway /app/gateway
COPY --from=builder /app/litellm/runtime/gateway /app/gateway
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

View file

@ -86,7 +86,7 @@
{{- $uiPaths = append $uiPaths (dict "path" "/*.txt" "pathType" "ImplementationSpecific") }}
{{- end }}
{{/*
Gateway data-plane prefixes — must mirror gateway/routes/allowlist.py.
Gateway data-plane prefixes — must mirror litellm/runtime/gateway/routes/allowlist.py.
Versioned paths are listed explicitly to avoid routing management routes
(e.g. /v1/access_group, /v2/key/info, /v1/tool/*, /v1/agents, /v1/workflows,
/v2/user/info, /v2/team/list, /v2/model/info, /v2/login, /v2/guardrails/*,

View file

@ -46,7 +46,7 @@ starts the process named by its first argument, so each workload only
differs in the argument the module passes
The load balancer routes gateway path prefixes (mirrored verbatim from
`gateway/routes/allowlist.py`) to the gateway, UI asset paths (`/`,
`litellm/runtime/gateway/routes/allowlist.py`) to the gateway, UI asset paths (`/`,
`/litellm-asset-prefix/*`, `/_next/*`, `/favicon.ico`) to the UI, and
everything else to the backend.

View file

@ -1,4 +1,4 @@
# Gateway path prefixes — mirrored verbatim from gateway/routes/allowlist.py
# Gateway path prefixes — mirrored verbatim from litellm/runtime/gateway/routes/allowlist.py
# and the helm ingress in litellm/deploy/helm/litellm/templates/ingress.yaml. Anything not in
# this list and not a UI asset path falls through to the backend (management
# API) catch-all rule on the ALB.

View file

@ -1,4 +1,4 @@
# Gateway path prefixes — mirrored verbatim from gateway/routes/allowlist.py
# Gateway path prefixes — mirrored verbatim from litellm/runtime/gateway/routes/allowlist.py
# and litellm/deploy/helm/litellm/templates/ingress.yaml. URL maps use the "path matcher"
# rule with `paths` lists; up to 10 path globs per rule, up to 50 rules
# per matcher. Easily fits the gateway list in one rule per chunk-of-10.

View file

@ -16,7 +16,7 @@ from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.types import Lifespan
# See gateway/main.py for why we assemble DATABASE_URL(s) here before
# See litellm/runtime/gateway/main.py for why we assemble DATABASE_URL(s) here before
# importing proxy_server.
from litellm.proxy.db.db_url_settings import DatabaseURLSettings
@ -45,7 +45,7 @@ def _is_backend_route(route) -> bool:
return any(path.startswith(prefix) for prefix in BACKEND_PATH_PREFIXES)
# See gateway/main.py for why the trim runs inside the lifespan instead of at
# See litellm/runtime/gateway/main.py for why the trim runs inside the lifespan instead of at
# module scope.
_proxy_lifespan: Final = app.router.lifespan_context

View file

@ -1,6 +1,6 @@
"""Gateway supervisor: assemble DATABASE_URL, start the in-container PgBouncer, then run uvicorn.
``gateway/main.py`` assembles ``DATABASE_URL`` inside every uvicorn worker, which
``litellm/runtime/gateway/main.py`` assembles ``DATABASE_URL`` inside every uvicorn worker, which
is fine for a plain Postgres URL but not for the pooler: PgBouncer must be
started exactly once per pod, before the workers fork, and the workers must be
handed the loopback URL it listens on. A pre-existing ``DATABASE_URL`` wins in

View file

@ -443,7 +443,7 @@ version_files = [
[tool.pytest.ini_options]
norecursedirs = ["*.egg", ".*", "_darcs", "build", "CVS", "dist", "node_modules", "venv", "{arch}", "**/litellm/ui", "**/litellm/vscode-extension", "**/litellm/enterprise", "**/litellm/proxy-extras", "**/litellm/packaging", "**/litellm/rust", "**/litellm/scripts", "**/litellm/runtime"]
pythonpath = ["litellm/scripts"]
pythonpath = ["litellm/scripts", "litellm/runtime"]
asyncio_mode = "auto"
asyncio_default_fixture_loop_scope = "session"
markers = [
@ -510,6 +510,7 @@ source = ["litellm"]
relative_files = true
[tool.ruff]
src = [".", "litellm/runtime"]
lint.ignore = ["F405", "E402", "F403"]
# The second group is the strict gate's graduates: rules the codebase already has zero
# violations of, so they hard-fail here instead of being counted by the strict gate.
@ -570,6 +571,7 @@ banned-module-level-imports = ["prisma"]
"litellm/proxy/utils.py" = ["F401"]
[tool.pyright]
extraPaths = ["litellm/runtime"]
include = ["litellm"]
ignore = []
exclude = ["**/node_modules", "**/__pycache__", "tests/e2e/claude_code", "tests/e2e_harness/claude_code", "tests/e2e/ui", "litellm/types/utils.py", "litellm/proxy/_types.py", "litellm/scripts", "litellm/runtime", "litellm/deploy", "litellm/ui", "litellm/vscode-extension", "litellm/enterprise", "litellm/proxy-extras", "litellm/packaging", "litellm/rust", "litellm/proxy/discovery_endpoints/skill_scanner_adapter"]

View file

@ -35,8 +35,10 @@ def test_a_uvicorn_worker_reimports_the_launcher_without_loading_litellm():
sys.executable,
"-I",
"-c",
'import runpy, sys; runpy.run_module("gateway.launch", run_name="__mp_main__", alter_sys=True); '
'import runpy, sys; sys.path.insert(0, sys.argv[1]); '
'runpy.run_module("gateway.launch", run_name="__mp_main__", alter_sys=True); '
'print("litellm" in sys.modules)',
str(Path(__file__).resolve().parents[3] / "litellm" / "runtime"),
],
capture_output=True,
text=True,

View file

@ -106,6 +106,7 @@ for _key, _previous in _PRE_DB_ENV.items():
_COVERAGE_PROBE: Final = """
import json, os, sys
sys.path.insert(0, os.environ["LITELLM_COMPONENT_ALLOWLIST_REPO_ROOT"])
sys.path.insert(0, os.path.join(os.environ["LITELLM_COMPONENT_ALLOWLIST_REPO_ROOT"], "litellm", "runtime"))
from starlette.routing import Mount
from litellm.runtime.backend.routes.allowlist import BACKEND_EXACT_PATHS, BACKEND_PATH_PREFIXES
from gateway.routes.allowlist import GATEWAY_EXACT_PATHS, GATEWAY_PATH_PREFIXES
@ -286,7 +287,7 @@ def test_gateway_plus_backend_covers_full_app():
uncovered: Final = report["uncovered"]
assert not uncovered, (
f"{len(uncovered)} route(s) are not exposed on either component. "
f"Update gateway/routes/allowlist.py or litellm/runtime/backend/routes/allowlist.py to cover:\n "
f"Update litellm/runtime/gateway/routes/allowlist.py or litellm/runtime/backend/routes/allowlist.py to cover:\n "
+ "\n ".join(uncovered)
)

View file

@ -147,6 +147,7 @@ def test_accepts_expected_release_wheel_tags(tmp_path: Path) -> None:
"litellm/packaging/litellm-core/pyproject.toml",
"litellm/scripts/default_branch.py",
"litellm/runtime/backend/main.py",
"litellm/runtime/gateway/launch.py",
"litellm/schema.prisma",
),
)