124 lines
4.0 KiB
Python
124 lines
4.0 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Startup banner shown when LMCache starts serving.
|
|
|
|
The banner is printed at most once per process, to ``stderr``, by the
|
|
``lmcache`` CLI and by the vLLM connector integrations (scheduler role
|
|
only, so tensor-parallel deployments print a single banner). Setting the
|
|
``LMCACHE_DISABLE_BANNER=1`` environment variable suppresses it.
|
|
"""
|
|
|
|
# Standard
|
|
from typing import TYPE_CHECKING
|
|
import os
|
|
|
|
if TYPE_CHECKING:
|
|
# Standard
|
|
from typing import TextIO
|
|
|
|
try:
|
|
# First Party
|
|
from lmcache import _version
|
|
|
|
_LMCACHE_VERSION = getattr(_version, "__version__", "unknown")
|
|
_LMCACHE_COMMIT = getattr(_version, "__commit_id__", "")
|
|
except ImportError: # pragma: no cover - version file is generated at build time
|
|
_LMCACHE_VERSION = "unknown"
|
|
_LMCACHE_COMMIT = ""
|
|
|
|
DISABLE_BANNER_ENV = "LMCACHE_DISABLE_BANNER"
|
|
|
|
LMCACHE_WEBSITE = "https://lmcache.ai/"
|
|
LMCACHE_RECIPES = "https://docs.lmcache.ai/recipes"
|
|
LMCACHE_LINKEDIN = "https://www.linkedin.com/company/lmcache-lab"
|
|
|
|
# Solarized palette, 24-bit ANSI escapes (TTY only): "LM" in bold italic
|
|
# orange (#cb4b16), "Cache" in cyan (#2aa198).
|
|
_LM_STYLE = "\x1b[1;3;38;2;203;75;22m"
|
|
_CACHE_STYLE = "\x1b[38;2;42;161;152m"
|
|
_DIM_STYLE = "\x1b[2m"
|
|
_RESET = "\x1b[0m"
|
|
|
|
# Figlet "standard" font, split into the two color groups.
|
|
_LM_ART = (
|
|
" _ __ __ ",
|
|
"| | | \\/ |",
|
|
"| | | |\\/| |",
|
|
"| |___| | | |",
|
|
"|_____|_| |_|",
|
|
)
|
|
_CACHE_ART = (
|
|
" ____ _ ",
|
|
" / ___|__ _ ___| |__ ___ ",
|
|
"| | / _` |/ __| '_ \\ / _ \\",
|
|
"| |__| (_| | (__| | | | __/",
|
|
" \\____\\__,_|\\___|_| |_|\\___|",
|
|
)
|
|
_RIGHT_TEXT_GAP = " "
|
|
|
|
_banner_printed = False
|
|
|
|
|
|
def _banner_disabled() -> bool:
|
|
"""Return whether ``LMCACHE_DISABLE_BANNER`` is set to a truthy value."""
|
|
return os.getenv(DISABLE_BANNER_ENV, "").strip().lower() in ("1", "true", "yes")
|
|
|
|
|
|
def _render_banner(colored: bool) -> str:
|
|
"""Render the banner text.
|
|
|
|
Args:
|
|
colored: Whether to wrap the logo in ANSI color escapes.
|
|
|
|
Returns:
|
|
The multi-line banner: the LMCache logo with the version (and
|
|
commit id when available), website, recipes, and LinkedIn links
|
|
on its right, and a final line describing the
|
|
``LMCACHE_DISABLE_BANNER`` opt-out. A blank line surrounds the
|
|
banner on each side to set it apart from adjacent log output.
|
|
"""
|
|
lm_style = _LM_STYLE if colored else ""
|
|
cache_style = _CACHE_STYLE if colored else ""
|
|
dim_style = _DIM_STYLE if colored else ""
|
|
reset = _RESET if colored else ""
|
|
|
|
version = f"LMCache v{_LMCACHE_VERSION}"
|
|
if _LMCACHE_COMMIT:
|
|
version += f" ({_LMCACHE_COMMIT[:9]})"
|
|
right_text = {
|
|
1: version,
|
|
2: f"Website: {LMCACHE_WEBSITE}",
|
|
3: f"Recipes: {LMCACHE_RECIPES}",
|
|
4: f"LinkedIn: {LMCACHE_LINKEDIN}",
|
|
}
|
|
lines = [""]
|
|
for row, (lm_part, cache_part) in enumerate(zip(_LM_ART, _CACHE_ART, strict=True)):
|
|
line = f"{lm_style}{lm_part}{reset} {cache_style}{cache_part}{reset}"
|
|
if row in right_text:
|
|
line += _RIGHT_TEXT_GAP + right_text[row]
|
|
lines.append(line)
|
|
lines.append(f"{dim_style}Set {DISABLE_BANNER_ENV}=1 to hide this banner.{reset}")
|
|
lines.append("")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def print_banner_once(stream: "TextIO") -> None:
|
|
"""Print the LMCache startup banner to ``stream`` at most once.
|
|
|
|
The banner shows the LMCache logo, version, and website, followed by
|
|
a hint describing the ``LMCACHE_DISABLE_BANNER`` opt-out. ANSI colors
|
|
are used only when ``stream`` is a TTY. Subsequent calls in the same
|
|
process are no-ops, as are all calls when ``LMCACHE_DISABLE_BANNER``
|
|
is set to ``1``/``true``/``yes``.
|
|
|
|
Args:
|
|
stream: Destination text stream. Callers should pass
|
|
``sys.stderr`` so the banner never interferes with
|
|
machine-readable stdout output.
|
|
"""
|
|
global _banner_printed
|
|
if _banner_printed or _banner_disabled():
|
|
return
|
|
_banner_printed = True
|
|
stream.write(_render_banner(stream.isatty()) + "\n")
|
|
stream.flush()
|