Skip to content

feat(sglang): gate chat-shaped Prometheus collectors on embedding worker (DIS-2107) - #9830

Merged
tzulingk merged 2 commits into
mainfrom
feat/per-worker-metric-gating
May 28, 2026
Merged

feat(sglang): gate chat-shaped Prometheus collectors on embedding worker (DIS-2107)#9830
tzulingk merged 2 commits into
mainfrom
feat/per-worker-metric-gating

Conversation

@tzulingk

@tzulingk tzulingk commented May 21, 2026

Copy link
Copy Markdown
Contributor

Overview:

Closes the SGLang half of DIS-2107. Operators monitoring an embedding fleet today see chat-shaped collectors emit zeros forever: KV gauges, prefill/decode counters, SGLang's sglang:* multiproc metrics, and the Dynamo LLMBackendMetrics series all live on every worker regardless of whether the engine even has a KV cache. This PR makes the SGLang publisher skip all of that wiring for embedding workers.

Details:

setup_sgl_metrics(engine, config, generate_endpoint) now short-circuits when config.dynamo_args.embedding_worker is True:

  • Not constructed: setup_prometheus_registry() (SGLang sglang:* multiproc metrics), LLMBackendMetrics() (total_blocks, gpu_cache_usage, model_load_time), DynamoSglangPublisher() (ZMQ scheduler pull + KV-event publisher + FPM relay).
  • Still returned: the function preserves its (publisher_or_None, asyncio.Task, metrics_labels) shape so init_embedding.py can keep the same await metrics_task + metrics_task.cancel() cleanup as the chat-worker path. The task is a never-completing waiter for uniformity.
  • Still set up by init_embedding.py: the embedding-shaped collectors (dynamo_embedding_batch_size, dynamo_embedding_input_tokens — see feat(sglang): Prometheus metrics for embedding workload shape (DIS-2094 part 1) #9753).

The result: on an embedding worker's /metrics, you see only the metrics that actually move on a pooling engine, not the always-zero chat noise.

Tests:

Two new cases in components/src/dynamo/sglang/tests/test_sglang_publisher.py:

  • test_setup_sgl_metrics_skips_chat_pipeline_for_embedding_worker — patches each chat-shaped constructor with a function that raises if invoked. With config.dynamo_args.embedding_worker=True, none of them must fire. Verifies the return tuple too: publisher is None, task is a cancellable asyncio.Task, metrics_labels still carries ("model", served_name).
  • test_setup_sgl_metrics_returns_publisher_for_chat_worker — sibling check that the chat-worker path is unchanged: register_engine_metrics_callback, LLMBackendMetrics, and DynamoSglangPublisher all fire exactly once. Confirms the gating doesn't accidentally short-circuit the default code path.

Why not vLLM in this PR?

vLLM's embedding-worker shape (DIS-2092 / PR #9713) hasn't merged yet, and the vLLM-side embedding observation PR (DIS-2094 part 3) is still pending. Once both land, the same gating will mirror to components/src/dynamo/vllm/publisher.py in a follow-up PR.

Where should the reviewer start?

  1. components/src/dynamo/sglang/publisher.py — the early-exit in setup_sgl_metrics(). The shape of the return tuple is preserved by design.
  2. components/src/dynamo/sglang/tests/test_sglang_publisher.py — the two new tests at the bottom of the file.

Linear: https://linear.app/nvidia/issue/DIS-2107


Open in Devin Review

Summary by CodeRabbit

  • New Features

    • Added support for embedding workers in metrics initialization, with proper resource cleanup handling.
  • Tests

    • Added test coverage for metrics setup behavior with embedding and non-embedding worker configurations.

Review Change Stack

Closes the first half of DIS-2107. The SGLang publisher previously
registered chat-shaped collectors (SGLang's multiprocess ``sglang:*``
metrics, the Dynamo ``LLMBackendMetrics`` gauges ``total_blocks`` /
``gpu_cache_usage`` / ``model_load_time``, the ``DynamoSglangPublisher``
with its ZMQ scheduler pull + KV-event + FPM-relay wiring) on every
worker, including embedding workers where they emit zeros forever.
Operators monitoring an embedding fleet saw noise instead of signal.

``setup_sgl_metrics`` now short-circuits when
``config.dynamo_args.embedding_worker`` is set: no chat-shaped
constructors run. It still returns a uniform
``(publisher_or_None, asyncio.Task, metrics_labels)`` shape so the
embedding init path can keep the same ``await + cancel()`` cleanup
sequence as the chat path.

The corresponding ``dynamo_embedding_batch_size`` /
``dynamo_embedding_input_tokens`` collectors that DO belong on an
embedding worker are registered separately by ``init_embedding.py`` via
``init_embedding_metrics`` (see PR #9753).

Tests
-----
``test_sglang_publisher.py`` gains two new cases:

- ``test_setup_sgl_metrics_skips_chat_pipeline_for_embedding_worker`` —
  patches ``setup_prometheus_registry``, ``register_engine_metrics_callback``,
  ``LLMBackendMetrics``, and ``DynamoSglangPublisher`` to ``raise`` if
  invoked. Setting ``embedding_worker=True`` must not trigger any of
  them, the return tuple must have ``publisher is None``, the task must
  be cancellable, and the model label still propagates.
- ``test_setup_sgl_metrics_returns_publisher_for_chat_worker`` — sibling
  check that the chat path is unchanged: all chat-shaped constructors
  fire and a publisher comes back.

vLLM publisher gating is intentionally NOT in this PR — vLLM has no
embedding-worker code path on ``main`` today. The mirror change lands
once the vLLM-side embedding worker (DIS-2092) merges and the
vLLM-side embedding observation PR (DIS-2094 part 3) is on deck.

Refs DIS-2107.

Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
@tzulingk
tzulingk requested review from a team as code owners May 21, 2026 16:58
@github-actions github-actions Bot added feat backend::sglang Relates to the sglang backend labels May 21, 2026
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds embedding worker support to setup_sglang_metrics by conditionally skipping the entire chat-shaped metrics and publisher construction pipeline. When enabled, the function returns a None publisher with a never-ending background task and proper metrics labels. The return type signature is updated to reflect the publisher may be None, and tests validate both the embedding and chat worker paths.

Changes

Embedding worker metrics bypass

Layer / File(s) Summary
Embedding worker metrics bypass in setup_sglang_metrics
components/src/dynamo/sglang/publisher.py
Adds an early-return path for embedding workers that skips Prometheus/KV-event wiring, constructs a never-ending asyncio task, and returns None for the publisher. Relocates metrics_labels initialization earlier so it is available for both embedding and non-embedding code paths. Updates return type to tuple[Optional[DynamoSglangPublisher], ...].
Test coverage for embedding and chat worker metric gating
components/src/dynamo/sglang/tests/test_sglang_publisher.py
Adds import of setup_sglang_metrics and two async tests validating metric construction gating: one confirms embedding workers fully short-circuit all chat-shaped constructors while returning publisher=None and proper labels; the other confirms chat workers still construct the publisher and chat metrics while gating off setup_prometheus_registry when enable_metrics is false. Both tests manage the never-ending task by canceling and awaiting it.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: gating chat-shaped Prometheus collectors for embedding workers in SGLang, directly reflecting the core objective of the PR.
Description check ✅ Passed The description comprehensively covers all required template sections with clear details about changes, rationale, test coverage, and includes proper issue tracking (Closes DIS-2107).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
components/src/dynamo/sglang/publisher.py (1)

493-493: ⚡ Quick win

Replace defensive getattr(..., "embedding_worker", False) with direct config.dynamo_args.embedding_worker

embedding_worker is declared on DynamoSGLangConfig and is populated from parsed CLI args (with --embedding-worker defaulting to False), so the getattr(..., default) silently masks missing/incorrect config. Use direct access to fail fast.

📝 Proposed change
-    if getattr(config.dynamo_args, "embedding_worker", False):
+    if config.dynamo_args.embedding_worker:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/src/dynamo/sglang/publisher.py` at line 493, Replace the defensive
getattr call with direct attribute access: where the code checks
getattr(config.dynamo_args, "embedding_worker", False) in publisher.py, use
config.dynamo_args.embedding_worker instead so missing/invalid configs raise
immediately; this targets the attribute on the DynamoSGLangConfig-backed
dynamo_args object and ensures the code fails fast rather than silently using a
default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/src/dynamo/sglang/publisher.py`:
- Around line 478-481: Remove the internal Linear ID from the docstring: replace
the `DIS-2094` reference with either a public GitHub issue reference (e.g.,
`#NNNN` or `GH-NNNN`) or reword to describe the cross-reference without a ticket
ID; update the sentence mentioning `init_embedding.py` and
`init_embedding_metrics` and the cleanup note about `metrics_task.cancel()` so
it reads the same but contains no `DIS-2094` token (e.g., "see init_embedding.py
/ init_embedding_metrics for embedding-shaped metrics registration" or "see
GH-NNNN" if you have a public issue).

In `@components/src/dynamo/sglang/tests/test_sglang_publisher.py`:
- Line 482: The inline comment contains an internal Linear ticket ID ("# ----
DIS-2107: per-worker metric gating ----"); remove the DIS-2107 reference and
replace it with an approved identifier or descriptive label (for example change
to a GitHub issue/PR number like "GH-1234" or simply "# ---- per-worker metric
gating ----") so the comment no longer contains internal Linear IDs; update the
comment text where it appears to use the new label.
- Around line 505-583: Re-run the code formatter so the test file meets the
project's Black settings: run the pre-commit black hook (e.g., pre-commit run
--all-files or black on the test file) and commit the reformatted file;
specifically wrap the long lines in the test function
test_setup_sgl_metrics_returns_publisher_for_chat_worker (e.g., the
_StubPublisher.metrics_publisher.create_endpoint lambda and any long
monkeypatch.setattr calls) so they comply with Black's line-length rules and
ensure the file passes pre-commit.

---

Nitpick comments:
In `@components/src/dynamo/sglang/publisher.py`:
- Line 493: Replace the defensive getattr call with direct attribute access:
where the code checks getattr(config.dynamo_args, "embedding_worker", False) in
publisher.py, use config.dynamo_args.embedding_worker instead so missing/invalid
configs raise immediately; this targets the attribute on the
DynamoSGLangConfig-backed dynamo_args object and ensures the code fails fast
rather than silently using a default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a5d91669-1fc7-4f95-82fb-ce825e142061

📥 Commits

Reviewing files that changed from the base of the PR and between 6e37679 and e1b83e8.

📒 Files selected for processing (2)
  • components/src/dynamo/sglang/publisher.py
  • components/src/dynamo/sglang/tests/test_sglang_publisher.py

Comment thread components/src/dynamo/sglang/publisher.py
Comment thread components/src/dynamo/sglang/tests/test_sglang_publisher.py Outdated
Comment thread components/src/dynamo/sglang/tests/test_sglang_publisher.py
Addresses three findings on PR #9830:

1. mypy: ``setup_sgl_metrics`` now declares ``Optional[DynamoSglangPublisher]``
   in its return tuple, but the chat-worker call sites (init_llm.py x2,
   init_diffusion.py x1) pass the publisher to functions that expect
   the non-Optional type. Add ``assert publisher is not None`` at
   those three call sites — those code paths are reached only when
   ``embedding_worker=False``, so the assertion is correct by
   construction and narrows the type for mypy.

2. mypy: the embedding-worker no-op task used
   ``asyncio.create_task(asyncio.Event().wait())``. ``Event.wait()``
   returns ``Literal[True]`` when set, which mypy then conflicts with
   the chat path's ``publisher.run() -> None`` return at the second
   ``create_task`` call. Wrap the wait in a local ``async _idle() ->
   None`` so both branches feed ``Coroutine[Any, Any, None]`` into
   ``create_task``.

3. Repository policy: scrub internal Linear ticket IDs from source.
   Removed ``DIS-2094`` from the publisher docstring and ``DIS-2107``
   from the test-file section header.

4. Black: re-format the two files mentioned by pre-commit.

No behavioral changes.

Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
@tzulingk
tzulingk requested a review from nnshah1 May 22, 2026 16:38
@tzulingk
tzulingk enabled auto-merge (squash) May 22, 2026 22:54
tzulingk added a commit that referenced this pull request May 27, 2026
Addresses three findings on PR #9830:

1. mypy: ``setup_sgl_metrics`` now declares ``Optional[DynamoSglangPublisher]``
   in its return tuple, but the chat-worker call sites (init_llm.py x2,
   init_diffusion.py x1) pass the publisher to functions that expect
   the non-Optional type. Add ``assert publisher is not None`` at
   those three call sites — those code paths are reached only when
   ``embedding_worker=False``, so the assertion is correct by
   construction and narrows the type for mypy.

2. mypy: the embedding-worker no-op task used
   ``asyncio.create_task(asyncio.Event().wait())``. ``Event.wait()``
   returns ``Literal[True]`` when set, which mypy then conflicts with
   the chat path's ``publisher.run() -> None`` return at the second
   ``create_task`` call. Wrap the wait in a local ``async _idle() ->
   None`` so both branches feed ``Coroutine[Any, Any, None]`` into
   ``create_task``.

3. Repository policy: scrub internal Linear ticket IDs from source.
   Removed ``DIS-2094`` from the publisher docstring and ``DIS-2107``
   from the test-file section header.

4. Black: re-format the two files mentioned by pre-commit.

No behavioral changes.

Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
(cherry picked from commit 2029e20)
@tzulingk
tzulingk requested review from dillon-cullinan and kthui May 28, 2026 17:12
@tzulingk
tzulingk merged commit fa1b570 into main May 28, 2026
81 checks passed
@tzulingk
tzulingk deleted the feat/per-worker-metric-gating branch May 28, 2026 17:15
tmonty12 pushed a commit that referenced this pull request Jun 8, 2026
…ker (DIS-2107) (#9830)

Signed-off-by: Tzu-Ling <tzulingk@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend::sglang Relates to the sglang backend feat size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants