feat(kv-events): expose structured KV-event publisher block on /server_info (#25844)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
06c23d55b5
commit
085777210c
@@ -256,10 +256,15 @@ class ZmqEventPublisher(EventPublisher):
|
||||
self._pub = self._ctx.socket(zmq.PUB)
|
||||
self._pub.set_hwm(self._hwm)
|
||||
# Heuristic: bind if wildcard / * present, else connect.
|
||||
# bind stable, connect volatile convention
|
||||
# bind stable, connect volatile convention.
|
||||
# ``0.0.0.0`` is the IPv4 bind-all wildcard alongside ``*``
|
||||
# and ``::``; ``/server_info`` advertises it as a wildcard,
|
||||
# so the publisher must bind it for the advertised endpoint
|
||||
# to actually be listening.
|
||||
if (
|
||||
"*" in self._endpoint
|
||||
or "::" in self._endpoint
|
||||
or "0.0.0.0" in self._endpoint
|
||||
or self._endpoint.startswith("ipc://")
|
||||
or self._endpoint.startswith("inproc://")
|
||||
):
|
||||
|
||||
@@ -636,12 +636,18 @@ async def server_info():
|
||||
await _global_state.tokenizer_manager.get_internal_state()
|
||||
)
|
||||
|
||||
server_args = _global_state.tokenizer_manager.server_args
|
||||
|
||||
# server_args.model_config is not serializable but should be excluded by asdict.
|
||||
return {
|
||||
**dataclasses.asdict(_global_state.tokenizer_manager.server_args),
|
||||
**dataclasses.asdict(server_args),
|
||||
**_global_state.scheduler_info,
|
||||
"internal_states": internal_states,
|
||||
"version": __version__,
|
||||
# Structured KV-event publisher descriptor for KV-aware routers.
|
||||
# `None` when publishing is disabled or misconfigured; see
|
||||
# `ServerArgs.describe_kv_events_publisher` for the precise contract.
|
||||
"kv_events": server_args.describe_kv_events_publisher(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7430,6 +7430,92 @@ class ServerArgs:
|
||||
else:
|
||||
return False
|
||||
|
||||
def describe_kv_events_publisher(self) -> Optional[dict]:
|
||||
"""Return a structured description of this server's KV-event
|
||||
publisher, or `None` if publishing is disabled / misconfigured.
|
||||
|
||||
This is the wire contract surfaced under the `kv_events` key on
|
||||
`/server_info` so KV-aware routers (e.g. the SGLang model
|
||||
gateway) can subscribe per-worker without operator-supplied port
|
||||
coordination. The router constructs the per-DP-rank SUB endpoint
|
||||
as ``tcp://<worker_host>:<endpoint_port_base + dp_rank>`` for
|
||||
every rank reported in ``dp_size``.
|
||||
|
||||
Returned descriptor shape:
|
||||
|
||||
{
|
||||
"publisher": "zmq",
|
||||
"endpoint_host": "*", # may be a ZMQ wildcard
|
||||
# ("*", "0.0.0.0", "::");
|
||||
# subscribers MUST substitute
|
||||
# the worker URL's host when
|
||||
# dialing
|
||||
"endpoint_port_base": 5557, # base TCP port; per-rank
|
||||
# port = base + dp_rank
|
||||
"topic": "", # ZMQ topic prefix on the
|
||||
# SUB filter (empty =
|
||||
# subscribe-all)
|
||||
"block_size": <page_size>, # subscribers MUST hash
|
||||
# prompts at this size
|
||||
"dp_size": <dp_size>, # number of SUB sockets
|
||||
# to open
|
||||
}
|
||||
|
||||
Returns ``None`` (i.e. "no publisher to describe") when any of:
|
||||
|
||||
* ``--kv-events-config`` is unset / empty / malformed JSON,
|
||||
* the configured publisher is ``"null"``,
|
||||
* ``page_size`` is missing or non-positive (a placeholder
|
||||
``block_size`` would cause silent KV-cache misses by hashing
|
||||
prompts at the wrong granularity on the router side),
|
||||
* the endpoint is not a routable TCP address (``inproc://`` /
|
||||
``ipc://``, missing port, non-integer port, or port outside
|
||||
``1..65535``).
|
||||
|
||||
Reuses ``KVEventsConfig.from_cli`` for JSON parsing; the inline
|
||||
``rfind(":")`` endpoint split mirrors
|
||||
``ZmqEventPublisher.offset_endpoint_port`` rather than adding a
|
||||
new module-level helper.
|
||||
"""
|
||||
# Lazy import so loading ``server_args`` doesn't pull in
|
||||
# disaggregation / msgspec / zmq at module top level.
|
||||
from sglang.srt.disaggregation.kv_events import KVEventsConfig
|
||||
|
||||
raw = self.kv_events_config
|
||||
page_size = self.page_size
|
||||
if not raw or page_size is None or page_size <= 0:
|
||||
return None
|
||||
try:
|
||||
cfg = KVEventsConfig.from_cli(raw)
|
||||
except Exception:
|
||||
# Malformed JSON / schema mismatch. The publisher would
|
||||
# have failed at server startup; ``/server_info`` must
|
||||
# keep working, so just report "no publisher" to consumers.
|
||||
return None
|
||||
if cfg.publisher == "null" or not cfg.endpoint:
|
||||
return None
|
||||
if not cfg.endpoint.startswith("tcp://"):
|
||||
return None
|
||||
body = cfg.endpoint[len("tcp://") :]
|
||||
last_colon = body.rfind(":")
|
||||
if last_colon < 0:
|
||||
return None
|
||||
host = body[:last_colon]
|
||||
try:
|
||||
port = int(body[last_colon + 1 :])
|
||||
except ValueError:
|
||||
return None
|
||||
if not host or not (0 < port < 65536):
|
||||
return None
|
||||
return {
|
||||
"publisher": cfg.publisher,
|
||||
"endpoint_host": host,
|
||||
"endpoint_port_base": port,
|
||||
"topic": cfg.topic,
|
||||
"block_size": page_size,
|
||||
"dp_size": self.dp_size,
|
||||
}
|
||||
|
||||
|
||||
# NOTE: This is a global variable to hold the server args for scheduler.
|
||||
_global_server_args: Optional[ServerArgs] = None
|
||||
|
||||
Reference in New Issue
Block a user