fix(proxy): keep show_banner() SDK-free, pick the JSON banner in initialize()

Since #46803 the `lite` shell calls show_banner() against an uninitialized
litellm package, and reading litellm.json_logs inside it initialized the whole
SDK at the banner (0.14 s to 0.83 s for a bare `lite` here, the seconds that PR
removed on small VMs). show_banner() is back to its main version; the proxy's
initialize() now emits the JSON banner record itself and calls show_banner()
only in plain mode. The banner unit tests move to the initialize() call site.
This commit is contained in:
yucheng-berri 2026-10-17 01:38:40 -07:00
parent 4fa139f8c0
commit e2303a3925
4 changed files with 41 additions and 44 deletions

View file

@ -11,12 +11,6 @@ LITELLM_BANNER: Final = """ ██╗ ██╗████████╗
def show_banner(): def show_banner():
"""Display the LiteLLM CLI banner.""" """Display the LiteLLM CLI banner."""
import litellm
from litellm._logging import print_startup_message
if litellm.json_logs:
print_startup_message(LITELLM_BANNER)
return
try: try:
import click import click

View file

@ -9550,9 +9550,14 @@ async def initialize(
master_key, \ master_key, \
user_custom_auth, \ user_custom_auth, \
prisma_client prisma_client
from litellm.proxy.common_utils.banner import show_banner from litellm.proxy.common_utils.banner import LITELLM_BANNER, show_banner
show_banner() # show_banner() also serves the `lite` shell, which runs against an uninitialized SDK (#46803), so
# the JSON-mode choice stays out of it.
if litellm.json_logs:
print_startup_message(LITELLM_BANNER)
else:
show_banner()
if os.getenv("LITELLM_DONT_SHOW_FEEDBACK_BOX", "").lower() != "true": if os.getenv("LITELLM_DONT_SHOW_FEEDBACK_BOX", "").lower() != "true":
generate_feedback_box() generate_feedback_box()
user_model = model user_model = model

View file

@ -1,36 +0,0 @@
"""The CLI banner is terminal art in plain mode and one JSON record when json_logs is on."""
import json
import sys
from typing import Final
import litellm
from litellm._logging import turn_on_json
from litellm.proxy.common_utils.banner import LITELLM_BANNER, show_banner
def test_show_banner_prints_the_banner_between_blank_lines_in_plain_mode(monkeypatch, capfd):
"""Operators without JSON logs see the banner exactly as before."""
monkeypatch.setattr(litellm, "json_logs", False)
show_banner()
assert capfd.readouterr() == (f"\n{LITELLM_BANNER}\n\n", "")
def test_show_banner_is_one_json_record_in_json_mode(monkeypatch, capfd):
"""With JSON logs on, the six banner rows are one INFO record with their indentation kept, so the
first line a worker writes parses like every other."""
monkeypatch.setattr(litellm, "json_logs", True)
monkeypatch.setattr(sys, "excepthook", sys.excepthook) # turn_on_json swaps it; put it back at teardown
turn_on_json()
show_banner()
out, err = capfd.readouterr()
assert err == ""
lines: Final = [raw for raw in out.splitlines() if raw.strip()]
assert len(lines) == 1, lines
record: Final = json.loads(lines[0])
assert record["message"] == LITELLM_BANNER
assert record["level"] == "INFO"

View file

@ -48,6 +48,7 @@ from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.proxy.hooks.parallel_request_limiter_v3 import RequestRateLimiterStash from litellm.proxy.hooks.parallel_request_limiter_v3 import RequestRateLimiterStash
from litellm.proxy.proxy_server import app, aws_error_type_exception_handler, initialize, openai_exception_handler from litellm.proxy.proxy_server import app, aws_error_type_exception_handler, initialize, openai_exception_handler
from litellm.utils import _invalidate_model_cost_lowercase_map from litellm.utils import _invalidate_model_cost_lowercase_map
from litellm.proxy.common_utils.banner import LITELLM_BANNER
from tests._master_key import MASTER_KEY from tests._master_key import MASTER_KEY
PUBLICLY_KNOWN_KEY: Final = "sk-" + "1234" PUBLICLY_KNOWN_KEY: Final = "sk-" + "1234"
@ -15725,3 +15726,36 @@ def test_feedback_box_is_one_json_record_in_json_mode(monkeypatch, capfd):
assert "Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new" in record["message"] assert "Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new" in record["message"]
assert "Thank you for using LiteLLM! - Krrish & Ishaan" in record["message"] assert "Thank you for using LiteLLM! - Krrish & Ishaan" in record["message"]
assert "\x1b[" not in record["message"] assert "\x1b[" not in record["message"]
@pytest.mark.asyncio
async def test_initialize_prints_the_banner_between_blank_lines_in_plain_mode(monkeypatch, capfd):
"""Operators without JSON logs see the banner exactly as before, through show_banner()'s flushing
click.echo."""
monkeypatch.setattr(litellm, "json_logs", False)
monkeypatch.setenv("LITELLM_DONT_SHOW_FEEDBACK_BOX", "true")
await proxy_server_module.initialize()
out, err = capfd.readouterr()
assert err == ""
assert out.startswith(f"\n{LITELLM_BANNER}\n\n"), out
@pytest.mark.asyncio
async def test_initialize_prints_the_banner_as_one_json_record_in_json_mode(monkeypatch, capfd):
"""With JSON logs on, the six banner rows are one INFO record with their indentation kept, so the
first line a worker writes parses like every other."""
monkeypatch.setattr(litellm, "json_logs", True)
monkeypatch.setattr(sys, "excepthook", sys.excepthook) # turn_on_json swaps it; put it back at teardown
monkeypatch.setenv("LITELLM_DONT_SHOW_FEEDBACK_BOX", "true")
turn_on_json()
await proxy_server_module.initialize()
out, err = capfd.readouterr()
assert err == ""
records: Final = [json.loads(raw) for raw in out.splitlines() if raw.strip()]
banner_records: Final = [record for record in records if record["message"] == LITELLM_BANNER]
assert len(banner_records) == 1, records
assert banner_records[0]["level"] == "INFO"