From 0e736a744215bc17ce808f3e25210320e7807d32 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 16 Feb 2026 17:01:28 -0800 Subject: [PATCH 1/6] add server root path test to github actions --- .github/workflows/test_server_root_path.yml | 309 ++++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 .github/workflows/test_server_root_path.yml diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml new file mode 100644 index 00000000000..ad5a35ebbec --- /dev/null +++ b/.github/workflows/test_server_root_path.yml @@ -0,0 +1,309 @@ +name: Test SERVER_ROOT_PATH + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + workflow_dispatch: + +jobs: + test-server-root-path: + runs-on: ubuntu-latest + timeout-minutes: 15 + + strategy: + matrix: + # Test multiple root paths to ensure flexibility + root_path: ["/api/v1", "/litellm", "/proxy"] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./docker/Dockerfile.database + tags: litellm-test:${{ github.sha }} + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Start LiteLLM container with SERVER_ROOT_PATH + run: | + docker run -d \ + --name litellm-test \ + -p 4000:4000 \ + -e SERVER_ROOT_PATH="${{ matrix.root_path }}" \ + -e LITELLM_MASTER_KEY="sk-1234" \ + litellm-test:${{ github.sha }} \ + --detailed_debug + + - name: Wait for container to be healthy + run: | + echo "Waiting for LiteLLM to start..." + max_attempts=30 + attempt=0 + + while [ $attempt -lt $max_attempts ]; do + if docker logs litellm-test 2>&1 | grep -q "Uvicorn running"; then + echo "✅ LiteLLM started successfully" + break + fi + attempt=$((attempt + 1)) + echo "Attempt $attempt/$max_attempts - waiting for server to start..." + sleep 2 + done + + if [ $attempt -eq $max_attempts ]; then + echo "❌ Server failed to start within timeout" + docker logs litellm-test + exit 1 + fi + + # Additional wait for full initialization + sleep 5 + + - name: Show container logs + if: always() + run: | + echo "=== Container Logs ===" + docker logs litellm-test + + - name: Test health endpoint with root path + run: | + ROOT_PATH="${{ matrix.root_path }}" + echo "Testing health endpoint at: http://localhost:4000${ROOT_PATH}/health" + + response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health") + + if [ "$response" = "200" ]; then + echo "✅ Health endpoint returned 200" + else + echo "❌ Health endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 + fi + + - name: Test health endpoint liveliness with root path + run: | + ROOT_PATH="${{ matrix.root_path }}" + echo "Testing liveliness endpoint at: http://localhost:4000${ROOT_PATH}/health/liveliness" + + response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health/liveliness") + + if [ "$response" = "200" ]; then + echo "✅ Liveliness endpoint returned 200" + else + echo "❌ Liveliness endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 + fi + + - name: Test health endpoint readiness with root path + run: | + ROOT_PATH="${{ matrix.root_path }}" + echo "Testing readiness endpoint at: http://localhost:4000${ROOT_PATH}/health/readiness" + + response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health/readiness") + + if [ "$response" = "200" ]; then + echo "✅ Readiness endpoint returned 200" + else + echo "❌ Readiness endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 + fi + + - name: Test UI is accessible with root path + run: | + ROOT_PATH="${{ matrix.root_path }}" + echo "Testing UI endpoint at: http://localhost:4000${ROOT_PATH}/ui" + + # Test that UI returns 200 or 30x (redirect) + response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/ui") + + if [[ "$response" =~ ^(200|30[0-9])$ ]]; then + echo "✅ UI endpoint returned $response" + else + echo "❌ UI endpoint returned $response (expected 200 or 30x)" + docker logs litellm-test + exit 1 + fi + + - name: Test UI index page content with root path + run: | + ROOT_PATH="${{ matrix.root_path }}" + echo "Testing UI content at: http://localhost:4000${ROOT_PATH}/ui/" + + # Fetch UI page and check for expected content + content=$(curl -sL "http://localhost:4000${ROOT_PATH}/ui/") + + # Check if the response contains typical HTML/UI markers + if echo "$content" | grep -q -E "(html|&1 | grep -q "Uvicorn running"; then + echo "✅ LiteLLM started successfully" + break + fi + attempt=$((attempt + 1)) + echo "Attempt $attempt/$max_attempts - waiting for server to start..." + sleep 2 + done + + if [ $attempt -eq $max_attempts ]; then + echo "❌ Server failed to start within timeout" + docker logs litellm-test + exit 1 + fi + + sleep 5 + + - name: Test default paths work without SERVER_ROOT_PATH + run: | + echo "Testing that default paths work when SERVER_ROOT_PATH is not set" + + # These should work at root level + health=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000/health") + ui=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000/ui") + + if [ "$health" = "200" ] && [[ "$ui" =~ ^(200|30[0-9])$ ]]; then + echo "✅ Default paths work correctly" + else + echo "❌ Default paths failed (health: $health, ui: $ui)" + docker logs litellm-test + exit 1 + fi + + - name: Cleanup + if: always() + run: | + docker stop litellm-test || true + docker rm litellm-test || true From 8ab2c91722a1a951664873fae467f761f9c4dbb9 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 16 Feb 2026 17:11:14 -0800 Subject: [PATCH 2/6] Update .github/workflows/test_server_root_path.yml Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .github/workflows/test_server_root_path.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index ad5a35ebbec..d26dda4d02b 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -2,13 +2,10 @@ name: Test SERVER_ROOT_PATH on: push: - branches: - main - - master pull_request: branches: - main - - master workflow_dispatch: jobs: From bec06c0e69e0b87fd1286ac5d5f698dc9383f344 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 16 Feb 2026 17:52:37 -0800 Subject: [PATCH 3/6] fixing syntax --- .github/workflows/test_server_root_path.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index ad5a35ebbec..fa57491db39 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -1,15 +1,10 @@ -name: Test SERVER_ROOT_PATH +name: Test Proxy SERVER_ROOT_PATH Routing +permissions: + contents: read on: - push: - branches: - - main - - master pull_request: - branches: - - main - - master - workflow_dispatch: + branches: [main] jobs: test-server-root-path: From 330802e85c82facd8877e12d8661594b86a1f0b7 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 16 Feb 2026 17:53:46 -0800 Subject: [PATCH 4/6] remove artifacts --- .github/workflows/test_server_root_path.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index ad6605a0296..fa57491db39 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -3,17 +3,8 @@ permissions: contents: read on: -<<<<<<< HEAD pull_request: branches: [main] -======= - push: - - main - pull_request: - branches: - - main - workflow_dispatch: ->>>>>>> 8ab2c91722a1a951664873fae467f761f9c4dbb9 jobs: test-server-root-path: From a3ff9030edf98b4c12033879d6cf9038fd793d53 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Mon, 16 Feb 2026 19:15:12 -0800 Subject: [PATCH 5/6] passing in masster key for api calls --- .github/workflows/test_server_root_path.yml | 207 +++++++++++--------- 1 file changed, 114 insertions(+), 93 deletions(-) diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index fa57491db39..9a13494b482 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -13,8 +13,7 @@ jobs: strategy: matrix: - # Test multiple root paths to ensure flexibility - root_path: ["/api/v1", "/litellm", "/proxy"] + root_path: ["/api/v1"] steps: - name: Checkout repository @@ -79,128 +78,147 @@ jobs: ROOT_PATH="${{ matrix.root_path }}" echo "Testing health endpoint at: http://localhost:4000${ROOT_PATH}/health" - response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health") - - if [ "$response" = "200" ]; then - echo "✅ Health endpoint returned 200" - else - echo "❌ Health endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - fi + for i in 1 2 3; do + response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health") + if [ "$response" = "200" ]; then + echo "✅ Health endpoint returned 200" + exit 0 + fi + echo "Attempt $i/3 - got $response, retrying in 5s..." + sleep 5 + done + echo "❌ Health endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 - name: Test health endpoint liveliness with root path run: | ROOT_PATH="${{ matrix.root_path }}" echo "Testing liveliness endpoint at: http://localhost:4000${ROOT_PATH}/health/liveliness" - response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health/liveliness") - - if [ "$response" = "200" ]; then - echo "✅ Liveliness endpoint returned 200" - else - echo "❌ Liveliness endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - fi + for i in 1 2 3; do + response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health/liveliness") + if [ "$response" = "200" ]; then + echo "✅ Liveliness endpoint returned 200" + exit 0 + fi + echo "Attempt $i/3 - got $response, retrying in 5s..." + sleep 5 + done + echo "❌ Liveliness endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 - name: Test health endpoint readiness with root path run: | ROOT_PATH="${{ matrix.root_path }}" echo "Testing readiness endpoint at: http://localhost:4000${ROOT_PATH}/health/readiness" - response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/health/readiness") - - if [ "$response" = "200" ]; then - echo "✅ Readiness endpoint returned 200" - else - echo "❌ Readiness endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - fi + for i in 1 2 3; do + response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health/readiness") + if [ "$response" = "200" ]; then + echo "✅ Readiness endpoint returned 200" + exit 0 + fi + echo "Attempt $i/3 - got $response, retrying in 5s..." + sleep 5 + done + echo "❌ Readiness endpoint returned $response (expected 200)" + docker logs litellm-test + exit 1 - name: Test UI is accessible with root path run: | ROOT_PATH="${{ matrix.root_path }}" echo "Testing UI endpoint at: http://localhost:4000${ROOT_PATH}/ui" - # Test that UI returns 200 or 30x (redirect) - response=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:4000${ROOT_PATH}/ui") - - if [[ "$response" =~ ^(200|30[0-9])$ ]]; then - echo "✅ UI endpoint returned $response" - else - echo "❌ UI endpoint returned $response (expected 200 or 30x)" - docker logs litellm-test - exit 1 - fi + for i in 1 2 3; do + response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/ui") + if [[ "$response" =~ ^(200|30[0-9])$ ]]; then + echo "✅ UI endpoint returned $response" + exit 0 + fi + echo "Attempt $i/3 - got $response, retrying in 5s..." + sleep 5 + done + echo "❌ UI endpoint returned $response (expected 200 or 30x)" + docker logs litellm-test + exit 1 - name: Test UI index page content with root path run: | ROOT_PATH="${{ matrix.root_path }}" echo "Testing UI content at: http://localhost:4000${ROOT_PATH}/ui/" - # Fetch UI page and check for expected content - content=$(curl -sL "http://localhost:4000${ROOT_PATH}/ui/") - - # Check if the response contains typical HTML/UI markers - if echo "$content" | grep -q -E "(html| Date: Mon, 16 Feb 2026 20:08:38 -0800 Subject: [PATCH 6/6] only tests for /ui --- .github/workflows/test_server_root_path.yml | 245 +------------------- 1 file changed, 8 insertions(+), 237 deletions(-) diff --git a/.github/workflows/test_server_root_path.yml b/.github/workflows/test_server_root_path.yml index 9a13494b482..bc559817503 100644 --- a/.github/workflows/test_server_root_path.yml +++ b/.github/workflows/test_server_root_path.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - root_path: ["/api/v1"] + root_path: ["/api/v1", "/llmproxy"] steps: - name: Checkout repository @@ -50,7 +50,7 @@ jobs: while [ $attempt -lt $max_attempts ]; do if docker logs litellm-test 2>&1 | grep -q "Uvicorn running"; then - echo "✅ LiteLLM started successfully" + echo "LiteLLM started successfully" break fi attempt=$((attempt + 1)) @@ -59,265 +59,36 @@ jobs: done if [ $attempt -eq $max_attempts ]; then - echo "❌ Server failed to start within timeout" + echo "Server failed to start within timeout" docker logs litellm-test exit 1 fi - # Additional wait for full initialization sleep 5 - name: Show container logs if: always() - run: | - echo "=== Container Logs ===" - docker logs litellm-test + run: docker logs litellm-test - - name: Test health endpoint with root path + - name: Test UI endpoint with root path run: | ROOT_PATH="${{ matrix.root_path }}" - echo "Testing health endpoint at: http://localhost:4000${ROOT_PATH}/health" - - for i in 1 2 3; do - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health") - if [ "$response" = "200" ]; then - echo "✅ Health endpoint returned 200" - exit 0 - fi - echo "Attempt $i/3 - got $response, retrying in 5s..." - sleep 5 - done - echo "❌ Health endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - - - name: Test health endpoint liveliness with root path - run: | - ROOT_PATH="${{ matrix.root_path }}" - echo "Testing liveliness endpoint at: http://localhost:4000${ROOT_PATH}/health/liveliness" - - for i in 1 2 3; do - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health/liveliness") - if [ "$response" = "200" ]; then - echo "✅ Liveliness endpoint returned 200" - exit 0 - fi - echo "Attempt $i/3 - got $response, retrying in 5s..." - sleep 5 - done - echo "❌ Liveliness endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - - - name: Test health endpoint readiness with root path - run: | - ROOT_PATH="${{ matrix.root_path }}" - echo "Testing readiness endpoint at: http://localhost:4000${ROOT_PATH}/health/readiness" - - for i in 1 2 3; do - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/health/readiness") - if [ "$response" = "200" ]; then - echo "✅ Readiness endpoint returned 200" - exit 0 - fi - echo "Attempt $i/3 - got $response, retrying in 5s..." - sleep 5 - done - echo "❌ Readiness endpoint returned $response (expected 200)" - docker logs litellm-test - exit 1 - - - name: Test UI is accessible with root path - run: | - ROOT_PATH="${{ matrix.root_path }}" - echo "Testing UI endpoint at: http://localhost:4000${ROOT_PATH}/ui" - - for i in 1 2 3; do - response=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/ui") - if [[ "$response" =~ ^(200|30[0-9])$ ]]; then - echo "✅ UI endpoint returned $response" - exit 0 - fi - echo "Attempt $i/3 - got $response, retrying in 5s..." - sleep 5 - done - echo "❌ UI endpoint returned $response (expected 200 or 30x)" - docker logs litellm-test - exit 1 - - - name: Test UI index page content with root path - run: | - ROOT_PATH="${{ matrix.root_path }}" - echo "Testing UI content at: http://localhost:4000${ROOT_PATH}/ui/" + echo "Testing UI at: http://localhost:4000${ROOT_PATH}/ui/" for i in 1 2 3; do content=$(curl -sL --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000${ROOT_PATH}/ui/") if echo "$content" | grep -q -E "(html|&1 | grep -q "Uvicorn running"; then - echo "✅ LiteLLM started successfully" - break - fi - attempt=$((attempt + 1)) - echo "Attempt $attempt/$max_attempts - waiting for server to start..." - sleep 2 - done - - if [ $attempt -eq $max_attempts ]; then - echo "❌ Server failed to start within timeout" - docker logs litellm-test - exit 1 - fi - - sleep 5 - - - name: Test default paths work without SERVER_ROOT_PATH - run: | - echo "Testing that default paths work when SERVER_ROOT_PATH is not set" - - for i in 1 2 3; do - health=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000/health") - ui=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -H "Authorization: Bearer sk-1234" "http://localhost:4000/ui") - - if [ "$health" = "200" ] && [[ "$ui" =~ ^(200|30[0-9])$ ]]; then - echo "✅ Default paths work correctly" - exit 0 - fi - echo "Attempt $i/3 - health: $health, ui: $ui, retrying in 5s..." - sleep 5 - done - echo "❌ Default paths failed (health: $health, ui: $ui)" - docker logs litellm-test - exit 1 - - name: Cleanup if: always() run: |