From ce705bb6dc75fc551bc3d6f6caa99a2f6f7c16a4 Mon Sep 17 00:00:00 2001 From: cctry Date: Fri, 24 Jul 2026 17:31:28 -0700 Subject: [PATCH] Report accelerator type in /v1/loads (#32348) Co-authored-by: Yinghai Lu --- python/sglang/srt/entrypoints/v1_loads.py | 11 ++++++++++- .../unit/entrypoints/test_v1_loads_aggregate.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/python/sglang/srt/entrypoints/v1_loads.py b/python/sglang/srt/entrypoints/v1_loads.py index c45f196e9..d940da1c3 100644 --- a/python/sglang/srt/entrypoints/v1_loads.py +++ b/python/sglang/srt/entrypoints/v1_loads.py @@ -20,16 +20,24 @@ metrics for load balancing, monitoring, and capacity planning. import time from datetime import datetime, timezone +from functools import lru_cache from typing import Optional from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import Response +from sglang.srt.utils import get_device_name from sglang.version import __version__ router = APIRouter() +@lru_cache(maxsize=1) +def _accelerator_name() -> Optional[str]: + """Accelerator marketing name (e.g. "NVIDIA GB300"), None if unavailable.""" + return get_device_name() + + def _get_tokenizer_manager(): """Dependency to get tokenizer_manager from global state.""" from sglang.srt.entrypoints.http_server import get_global_state @@ -87,7 +95,7 @@ async def get_loads( format: Response format - 'json' (default) or 'prometheus' Returns: - JSON response with timestamp, version, and per-DP-rank loads + JSON response with timestamp, version, accelerator, and per-DP-rank loads """ include_list = [s.strip() for s in include.split(",")] if include else None @@ -119,5 +127,6 @@ async def get_loads( return { "timestamp": datetime.now(timezone.utc).isoformat(), "version": __version__, + "accelerator": _accelerator_name(), "loads": loads, } diff --git a/test/registered/unit/entrypoints/test_v1_loads_aggregate.py b/test/registered/unit/entrypoints/test_v1_loads_aggregate.py index e4e9240d8..4aea88daf 100644 --- a/test/registered/unit/entrypoints/test_v1_loads_aggregate.py +++ b/test/registered/unit/entrypoints/test_v1_loads_aggregate.py @@ -5,9 +5,11 @@ import os import tempfile import unittest from types import SimpleNamespace +from unittest import mock import msgspec.msgpack +from sglang.srt.entrypoints import v1_loads from sglang.srt.entrypoints.v1_loads import get_loads from sglang.srt.managers.load_snapshot import ( HEADER_STRUCT, @@ -91,6 +93,19 @@ class TestLoadsResponse(CustomTestCase): self.assertEqual(response["loads"][0]["num_waiting_reqs"], 2) +class TestLoadsAcceleratorField(CustomTestCase): + def test_accelerator_reported_in_json(self): + """Guards the response contract: the JSON envelope carries an + "accelerator" field with the detected device name.""" + manager = _FakeHttpTokenizerManager([LoadSnapshot(dp_rank=0)]) + + with mock.patch.object( + v1_loads, "_accelerator_name", return_value="NVIDIA GB300" + ): + response = asyncio.run(get_loads(tokenizer_manager=manager)) + self.assertEqual(response["accelerator"], "NVIDIA GB300") + + class TestGetLoads(CustomTestCase): def test_load_snapshot_wire_format_is_msgpack_slots(self): path = _temp_path()