fix: server rooth path (#19790)

This commit is contained in:
Harshit Jain
2026-01-26 09:48:06 -08:00
committed by GitHub
parent 8ece284ca3
commit b920be3ee7
3 changed files with 82 additions and 9 deletions
+1
View File
@@ -825,6 +825,7 @@ app = FastAPI(
title=_title,
description=_description,
version=version,
root_path=server_root_path,
lifespan=proxy_startup_event, # type: ignore[reportGeneralTypeIssues]
)
+17 -9
View File
@@ -982,7 +982,9 @@ class ProxyLogging:
try:
# Check if load balancing should be used
if guardrail_name and self._should_use_guardrail_load_balancing(guardrail_name):
if guardrail_name and self._should_use_guardrail_load_balancing(
guardrail_name
):
response = await self._execute_guardrail_with_load_balancing(
guardrail_name=guardrail_name,
hook_type="pre_call",
@@ -1017,7 +1019,11 @@ class ProxyLogging:
latency_seconds = guardrail_end_time - guardrail_start_time
# Get guardrail name for metrics (fallback if not set)
metrics_guardrail_name = guardrail_name or getattr(callback, "guardrail_name", callback.__class__.__name__) or "unknown"
metrics_guardrail_name = (
guardrail_name
or getattr(callback, "guardrail_name", callback.__class__.__name__)
or "unknown"
)
# Find PrometheusLogger in callbacks and record metrics
for prom_callback in litellm.callbacks:
@@ -2218,17 +2224,17 @@ class PrismaClient:
) -> Optional[dict]:
"""
Execute a query with automatic fallback for PostgreSQL cached plan errors.
This handles the "cached plan must not change result type" error that occurs
during rolling deployments when schema changes are applied while old pods
still have cached query plans expecting the old schema.
Args:
sql_query: SQL query string to execute
Returns:
Query result or None
Raises:
Original exception if not a cached plan error
"""
@@ -2241,7 +2247,7 @@ class PrismaClient:
# Add a unique comment to make the query different
sql_query_retry = sql_query.replace(
"SELECT",
f"SELECT /* cache_invalidated_{int(time.time() * 1000)} */"
f"SELECT /* cache_invalidated_{int(time.time() * 1000)} */",
)
verbose_proxy_logger.warning(
"PostgreSQL cached plan error detected for token lookup, "
@@ -2583,7 +2589,9 @@ class PrismaClient:
WHERE v.token = '{token}'
"""
response = await self._query_first_with_cached_plan_fallback(sql_query)
response = await self._query_first_with_cached_plan_fallback(
sql_query
)
if response is not None:
if response["team_models"] is None:
@@ -4227,7 +4235,7 @@ def get_server_root_path() -> str:
- If SERVER_ROOT_PATH is set, return it.
- Otherwise, default to "/".
"""
return os.getenv("SERVER_ROOT_PATH", "/")
return os.getenv("SERVER_ROOT_PATH", "")
def get_prisma_client_or_throw(message: str):
@@ -0,0 +1,64 @@
import os
from unittest import mock
from litellm.proxy import utils
# Test the utility function logic
def test_get_server_root_path_unset():
"""
Test that get_server_root_path returns empty string when SERVER_ROOT_PATH is unset
"""
with mock.patch.dict(os.environ, {}, clear=True):
# We need to make sure SERVER_ROOT_PATH is not in env
if "SERVER_ROOT_PATH" in os.environ:
del os.environ["SERVER_ROOT_PATH"]
root_path = utils.get_server_root_path()
assert (
root_path == ""
), "Should return empty string when unset to allow X-Forwarded-Prefix"
def test_get_server_root_path_set():
"""
Test that get_server_root_path returns the value when SERVER_ROOT_PATH is set
"""
with mock.patch.dict(os.environ, {"SERVER_ROOT_PATH": "/my-path"}, clear=True):
root_path = utils.get_server_root_path()
assert root_path == "/my-path", "Should return the set value"
def test_get_server_root_path_empty_string():
"""
Test that get_server_root_path returns empty string when SERVER_ROOT_PATH is explicitly empty
"""
with mock.patch.dict(os.environ, {"SERVER_ROOT_PATH": ""}, clear=True):
root_path = utils.get_server_root_path()
assert (
root_path == ""
), "Should return empty string when explicitly set to empty"
# Integration test simulation for FastAPI app initialization
def test_fastapi_app_initialization_mock():
"""
Simulate how proxy_server.py initializes FastAPI app with the root_path.
We don't import proxy_server because it has global side effects/singletons.
Instead we verify the logic flow.
"""
from fastapi import FastAPI
# CASE 1: Proxy Mode (Unset)
with mock.patch.dict(os.environ, {}, clear=True):
if "SERVER_ROOT_PATH" in os.environ:
del os.environ["SERVER_ROOT_PATH"]
server_root_path = utils.get_server_root_path()
app = FastAPI(root_path=server_root_path)
assert app.root_path == ""
# CASE 2: Direct Mode (Set)
with mock.patch.dict(os.environ, {"SERVER_ROOT_PATH": "/custom-root"}, clear=True):
server_root_path = utils.get_server_root_path()
app = FastAPI(root_path=server_root_path)
assert app.root_path == "/custom-root"