Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,48 @@ jobs:
ls
python -m pytest -vv tests/otel_tests -x --junitxml=test-results/junit.xml --durations=5
no_output_timeout: 120m
# Clean up first container
- run:
name: Stop and remove first container
command: |
docker stop my-app
docker rm my-app

# Second Docker Container Run with Different Config
# NOTE: We intentionally pass a "bad" license here. We need to ensure proxy starts and serves request even with bad license
- run:
name: Run Second Docker container
command: |
docker run -d \
-p 4000:4000 \
-e DATABASE_URL=$PROXY_DATABASE_URL \
-e REDIS_HOST=$REDIS_HOST \
-e REDIS_PASSWORD=$REDIS_PASSWORD \
-e REDIS_PORT=$REDIS_PORT \
-e LITELLM_MASTER_KEY="sk-1234" \
-e OPENAI_API_KEY=$OPENAI_API_KEY \
-e LITELLM_LICENSE="bad-license" \
--name my-app-3 \
-v $(pwd)/litellm/proxy/example_config_yaml/enterprise_config.yaml:/app/config.yaml \
my-app:latest \
--config /app/config.yaml \
--port 4000 \
--detailed_debug

- run:
name: Start outputting logs for second container
command: docker logs -f my-app-2
background: true

- run:
name: Wait for second app to be ready
command: dockerize -wait http://localhost:4000 -timeout 5m

- run:
name: Run second round of tests
command: |
python -m pytest -vv tests/basic_proxy_startup_tests -x --junitxml=test-results/junit-2.xml --durations=5
no_output_timeout: 120m

# Store test results
- store_test_results:
Expand Down
4 changes: 3 additions & 1 deletion litellm/proxy/common_utils/callback_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,9 @@ def initialize_callbacks_on_proxy( # noqa: PLR0915

if "prometheus" in value:
if premium_user is not True:
raise Exception(CommonProxyErrors.not_premium_user.value)
verbose_proxy_logger.warning(
f"Prometheus metrics are only available for premium users. {CommonProxyErrors.not_premium_user.value}"
)
Comment on lines +245 to +247
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

category Logging

Revise user-facing warning message for clarity.

Tell me more

Logging issue identified: The warning message Prometheus metrics are only available for premium users. {CommonProxyErrors.not_premium_user.value} might be confusing for end-users as it includes system-centric error codes. It would be more helpful to tailor this message towards the user's perspective, indicating that they cannot access Prometheus metrics due to their non-premium status. Please consider revising this log message for better clarity and user experience.

Chat with Korbit by mentioning @korbit-ai, and give a 👍 or 👎 to help Korbit improve your reviews.

from litellm.proxy.proxy_server import app

verbose_proxy_logger.debug("Starting Prometheus Metrics on /metrics")
Expand Down
17 changes: 17 additions & 0 deletions litellm/proxy/example_config_yaml/enterprise_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
model_list:
- model_name: gpt-4
litellm_params:
model: openai/fake
api_key: fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
tags: ["teamA"]
model_info:
id: "team-a-model"

litellm_settings:
cache: true
callbacks: ["prometheus"]

router_settings:
enable_tag_filtering: True # 👈 Key Change

51 changes: 51 additions & 0 deletions tests/basic_proxy_startup_tests/test_basic_proxy_startup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
This test ensures that the proxy starts and serves requests even with a bad license.


in ci/cd config.yml, we set the license to "bad-license"
"""

import pytest
import aiohttp
from typing import Optional


@pytest.mark.asyncio
async def test_health_and_chat_completion():
"""
Test health endpoints and chat completion:
1. Check /health/readiness
2. Check /health/liveness
3. Make a chat completion call
"""
async with aiohttp.ClientSession() as session:
# Test readiness endpoint
async with session.get("http://0.0.0.0:4000/health/readiness") as response:
assert response.status == 200
readiness_response = await response.json()
assert readiness_response["status"] == "connected"

# Test liveness endpoint
async with session.get("http://0.0.0.0:4000/health/liveness") as response:
assert response.status == 200
liveness_response = await response.json()
print("liveness_response", liveness_response)

# Make a chat completion call
url = "http://0.0.0.0:4000/chat/completions"
headers = {
"Authorization": "Bearer sk-1234",
"Content-Type": "application/json",
}
data = {
"model": "gpt-4",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"},
],
}

async with session.post(url, headers=headers, json=data) as response:
assert response.status == 200
completion_response = await response.json()
assert "choices" in completion_response