Skip to content

Conversation

biswapanda
Copy link
Contributor

@biswapanda biswapanda commented Aug 16, 2025

Overview:

dynamo namespace scoping for backend components

closes: DYN-838

Supports scoping backend to a specific dynamo namespace based on env variable DYN_NAMESPACE

[NOTE] Merge after related PR for frontend: #2394

Summary by CodeRabbit

  • New Features

    • Namespace-aware model discovery: filter models by a specific namespace or use global discovery.
    • Frontend now supports setting a namespace via CLI option or environment variable, with clear startup logging indicating discovery mode.
  • Documentation

    • Updated vLLM worker documentation to remove an obsolete endpoint flag and clarify remaining CLI options.
  • Tests

    • Added integration and unit tests covering namespace filtering, endpoint parsing, and discovery scoping to ensure reliable behavior across scenarios.

Copy link
Contributor

coderabbitai bot commented Aug 16, 2025

Walkthrough

Introduces namespace-aware discovery and filtering across backend and frontend, removes the vLLM --endpoint CLI flag, centralizes default namespace (“global”) via DYN_NAMESPACE, adjusts watcher APIs to accept an optional target namespace, refactors HTTP discovery to a config-driven watcher with endpoint enable/disable updates, adds a new namespace module, and updates docs/tests.

Changes

Cohort / File(s) Summary
Docs: remove vLLM --endpoint flag
components/backends/vllm/README.md, components/backends/vllm/deploy/README.md
Deleted CLI docs for --endpoint; remaining flags unchanged.
vLLM backend args: endpoint removal & namespace defaults
components/backends/vllm/src/dynamo/vllm/args.py
Dropped DEFAULT_ENDPOINT and --endpoint CLI; config now derives namespace from DYN_NAMESPACE (default “global”), component from is_prefill_worker, and endpoint fixed to “generate”. Removed endpoint parsing/validation and sys.exit path.
Frontend: namespace-aware endpoint construction & logging
components/frontend/src/dynamo/frontend/main.py
Added GLOBAL_NAMESPACE, DYNAMO_NAMESPACE_ENV_VAR, logger, and --namespace CLI. Endpoint ID defaults to ".frontend.http" or "global.frontend.http". Logs discovery mode.
Watcher API: namespace filtering
lib/llm/src/discovery/watcher.rs
ModelWatcher::watch now takes target_namespace: Option<&str>; filters events by namespace; enhanced logging.
Entrypoint (common): arity update
lib/llm/src/entrypoint/input/common.rs
Updated call to watch(receiver, None). No logic change.
HTTP discovery refactor: config-driven watcher + endpoint updates
lib/llm/src/entrypoint/input/http.rs
Added namespace determination, WatcherConfig, refactored run_watcher(config), namespace-scoped etcd prefix watch, and update_http_endpoints to enable/disable endpoints on ModelUpdate.
Library surface: namespace module
lib/llm/src/lib.rs, lib/llm/src/namespace.rs
Exposed new namespace module with GLOBAL_NAMESPACE and is_global_namespace.
Tests: namespace behavior and HTTP integration
lib/llm/tests/http_namespace_integration.rs, lib/llm/tests/namespace.rs
Added tests for namespace filtering, endpoint parsing, discovery scoping, and serialization paths.

Sequence Diagram(s)

sequenceDiagram
  participant Env as Env/DYN_NAMESPACE
  participant Frontend as Frontend CLI/Main
  participant Etcd as etcd Watch
  participant Watcher as ModelWatcher
  participant Router as HTTP Router
  participant Services as HTTP Endpoints

  Env->>Frontend: Read DYN_NAMESPACE / --namespace
  Frontend->>Frontend: Build endpoint_id = "<ns>.frontend.http" (default global)
  Frontend->>Etcd: Start watch on MODEL_ROOT_PATH (prefix)
  Frontend->>Watcher: watch(receiver, target_namespace)
  Etcd-->>Watcher: Put/Delete(ModelEntry w/ namespace)
  Watcher->>Watcher: Filter by target_namespace (if non-global)
  Watcher-->>Frontend: ModelUpdate(Add/Remove, scoped)
  Frontend->>Router: update_http_endpoints(ModelUpdate)
  Router->>Services: Enable/Disable Chat/Completion/Embedding...
Loading
sequenceDiagram
  participant vLLM as vLLM Worker
  participant Args as args.py
  participant Config as Worker Config

  vLLM->>Args: Parse CLI (no --endpoint)
  Args->>Args: namespace = env DYN_NAMESPACE or "global"
  Args->>Args: component = prefill ? "prefill" : "backend"
  Args->>Config: endpoint = "generate"
  Config-->>vLLM: Initialized without endpoint parsing
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I twitch my ears at names that roll,
From global fields to burrowed goal.
Watchers sift the meadow’s calls,
Only blooms from right hedgerows fall.
Endpoints hop—enable, rest—
In namespaces, we build the nest. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (8)
components/backends/vllm/src/dynamo/vllm/args.py (1)

108-110: Guard empty/whitespace DYN_NAMESPACE and improve startup observability

Normalize the environment variable, defaulting to "global" if it’s empty or only whitespace, and emit an info-level log of the resolved settings:

--- a/components/backends/vllm/src/dynamo/vllm/args.py
+++ b/components/backends/vllm/src/dynamo/vllm/args.py
@@ -108,3 +108,7 @@
-    config.namespace = os.environ.get("DYN_NAMESPACE", "global")
+    ns = os.environ.get("DYN_NAMESPACE", "").strip()
+    config.namespace = ns if ns else "global"
     config.component = "prefill" if args.is_prefill_worker else "backend"
     config.endpoint = "generate"
+
+# Log resolved namespace/component/endpoint for easier debugging
+logger.info(
+    "Dynamo namespace=%s (component=%s, endpoint=%s)",
+    config.namespace,
+    config.component,
+    config.endpoint,
+)

• Confirmed that this module no longer references any --endpoint flags or DEFAULT_ENDPOINT constants.
• Other parts of the repo continue to manage their own endpoint flags independently.

[optional_refactors_recommended]

components/frontend/src/dynamo/frontend/main.py (3)

41-45: Avoid cross-language drift on the global namespace literal.

Python and Rust both define "global" separately now. Consider consolidating Python’s namespace constants in a single module (e.g., dynamo.frontend.namespace or a shared Python util) to reduce duplication and drift, even if Rust remains authoritative.


125-130: Update help text to reflect env-var fallback.

The help says “If not specified, discovers models from all namespaces,” but parse_args will use DYN_NAMESPACE if set. Align the help text to prevent confusion.

Apply:

-        help="Dynamo namespace for model discovery scoping. If specified, models will only be discovered from this namespace. If not specified, discovers models from all namespaces (global discovery).",
+        help="Dynamo namespace for model discovery scoping. If set, discovery is scoped to this namespace. If omitted, uses the DYN_NAMESPACE env var when present; otherwise uses global discovery.",

165-167: Guard against empty/whitespace env var values.

Strip the env var and only apply it when non-empty. Keeps semantics clear and avoids accidental empty namespace selection.

Apply:

-    if not flags.namespace:
-        flags.namespace = os.environ.get(DYNAMO_NAMESPACE_ENV_VAR)
+    ns_env = os.environ.get(DYNAMO_NAMESPACE_ENV_VAR, "").strip()
+    if not flags.namespace and ns_env:
+        flags.namespace = ns_env
lib/llm/src/discovery/watcher.rs (2)

161-166: Prefer structured fields over string interpolation in tracing::error

Structured fields improve log querying and avoid format overhead when the level is disabled.

Apply this diff:

-                            tracing::error!(
-                                error = format!("{err:#}"),
-                                "error adding model {} from namespace {}",
-                                model_entry.name,
-                                model_entry.endpoint.namespace,
-                            );
+                            tracing::error!(
+                                error = %err,
+                                model_name = %model_entry.name,
+                                namespace = %model_entry.endpoint.namespace,
+                                "error adding model"
+                            );

189-194: Don’t treat missing entries on Delete as errors when filtering is active

When a non-target namespace key is deleted, it was never stored due to Put filtering; handle_delete will currently bail and emit error logs. Downgrade to a debug and return Ok(None) to avoid log noise.

Apply this diff:

-        let model_entry = match self.manager.remove_model_entry(key) {
-            Some(entry) => entry,
-            None => {
-                anyhow::bail!("Missing ModelEntry for {key}");
-            }
-        };
+        let model_entry = match self.manager.remove_model_entry(key) {
+            Some(entry) => entry,
+            None => {
+                tracing::debug!(
+                    "Delete for unknown model entry key {key}; likely out-of-scope namespace"
+                );
+                return Ok(None);
+            }
+        };
lib/llm/src/entrypoint/input/http.rs (1)

199-200: Include target_namespace in watch startup log

This improves observability when diagnosing discovery scope issues.

Apply this diff:

-    tracing::info!("Watching for remote model at {}", config.network_prefix);
+    tracing::info!(
+        target_namespace = ?config.target_namespace,
+        "Watching for remote model at {}",
+        config.network_prefix
+    );
lib/llm/tests/namespace.rs (1)

49-67: DRY opportunity for create_test_model_entry across test modules

Both this file and http_namespace_integration.rs implement the same helper. Consider moving it into a small tests/common module to reduce duplication.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fb10ffb and 52a0275.

📒 Files selected for processing (11)
  • components/backends/vllm/README.md (0 hunks)
  • components/backends/vllm/deploy/README.md (0 hunks)
  • components/backends/vllm/src/dynamo/vllm/args.py (1 hunks)
  • components/frontend/src/dynamo/frontend/main.py (6 hunks)
  • lib/llm/src/discovery/watcher.rs (4 hunks)
  • lib/llm/src/entrypoint/input/common.rs (1 hunks)
  • lib/llm/src/entrypoint/input/http.rs (5 hunks)
  • lib/llm/src/lib.rs (1 hunks)
  • lib/llm/src/namespace.rs (1 hunks)
  • lib/llm/tests/http_namespace_integration.rs (1 hunks)
  • lib/llm/tests/namespace.rs (1 hunks)
💤 Files with no reviewable changes (2)
  • components/backends/vllm/README.md
  • components/backends/vllm/deploy/README.md
🧰 Additional context used
🧬 Code Graph Analysis (5)
lib/llm/src/lib.rs (5)
lib/runtime/src/distributed.rs (1)
  • namespace (185-187)
lib/runtime/src/component.rs (2)
  • namespace (218-220)
  • namespace (602-609)
lib/bindings/python/rust/lib.rs (1)
  • namespace (286-291)
lib/bindings/python/src/dynamo/_core.pyi (1)
  • namespace (37-41)
lib/llm/src/kv_router/publisher.rs (1)
  • namespace (988-989)
components/frontend/src/dynamo/frontend/main.py (1)
lib/runtime/src/component/client.rs (1)
  • is_static (129-131)
lib/llm/tests/http_namespace_integration.rs (1)
lib/llm/src/namespace.rs (1)
  • is_global_namespace (7-9)
lib/llm/src/discovery/watcher.rs (1)
lib/llm/src/namespace.rs (1)
  • is_global_namespace (7-9)
lib/llm/src/entrypoint/input/http.rs (3)
lib/llm/src/namespace.rs (1)
  • is_global_namespace (7-9)
lib/llm/src/http/service/service_v2.rs (2)
  • etcd_client (111-113)
  • new (71-83)
lib/llm/src/discovery/watcher.rs (1)
  • new (60-74)
🔇 Additional comments (17)
components/frontend/src/dynamo/frontend/main.py (3)

22-22: LGTM on logging import.

Importing logging at module scope matches the new startup logs.


206-215: Dynamic endpoint construction is consistent with watcher semantics.

Using f"{namespace}.frontend.http" and falling back to f"{GLOBAL_NAMESPACE}.frontend.http" aligns with is_global_namespace behavior on the Rust side.


229-241: Startup logs are useful and well-scoped.

The logs clearly indicate static vs. dynamic discovery and the namespace scope. This will help triage namespace issues in multi-tenant deployments.

lib/llm/src/lib.rs (1)

30-30: Expose namespace module public API.

The new pub mod namespace; is correctly placed and expands the crate’s public surface for shared namespace semantics.

lib/llm/src/entrypoint/input/common.rs (1)

78-81: It looks like only the common.rs entrypoint is spawning a watcher with global scope today. The other Dynamic branches in http.rs and endpoint.rs do not call watch(...), so they aren’t affected by the namespace scoping change.

  • We only need to update lib/llm/src/entrypoint/input/common.rs.
  • The proposed diff is safe: it captures DYN_NAMESPACE once, converts it to Option<&str>, and passes that into watch(...).

Unless you intend this watcher to remain unscoped (e.g. strictly for dev/debug), go ahead and merge. If you do need a global-only watcher there, please call out that exception explicitly in a comment or confirm here.

lib/llm/src/namespace.rs (1)

1-9: LGTM: clear, minimal shared namespace semantics.

The constant and helper match the intended global discovery behavior and are easy to consume across modules.

lib/llm/src/discovery/watcher.rs (2)

113-125: Namespace-aware filtering on Put events looks correct

Filtering before persisting entries avoids cross-namespace bleed-through. The debug log with model/namespace context is helpful for observability.


91-95: No compatibility issue with Option::is_none_or

The project’s rust-toolchain is set to Rust 1.87.0, and Option::is_none_or was stabilized in Rust 1.82.0, so it’s fully supported. No changes are required here—please disregard the suggestion to replace it with map_or.

Likely an incorrect or invalid review comment.

lib/llm/src/entrypoint/input/http.rs (3)

42-48: Deriving target_namespace from the local model’s namespace is sound

Using is_global_namespace to decide whether to scope discovery is consistent with the watcher’s semantics and the tests. Good.


229-267: Endpoint enable/disable logic aligns with ModelUpdate semantics

Mapping Backend to both Chat and Completion and deferring others to as_endpoint_type keeps behavior consistent as backends front multiple routes. Looks good.


48-57: No namespace segment in etcd keys — skip narrowing the watched prefix

After inspecting how model entries are written, their etcd keys are generated with ModelNetworkName::new() as

format!("{MODEL_ROOT_PATH}/{}", uuid::Uuid::new_v4())

i.e.
“models/”
The namespace lives only inside the JSON value, not in the key path. The watcher already filters by namespace in code (model_entry.endpoint.namespace != target_ns), so narrowing the etcd prefix to “models/{namespace}” wouldn’t match anything. You can ignore the suggested prefix change.

Likely an incorrect or invalid review comment.

lib/llm/tests/namespace.rs (2)

11-47: Solid coverage of global namespace semantics

Good assertions around GLOBAL constant, empty string handling, whitespace non-global, and case sensitivity.


102-165: Namespace filtering test mirrors watcher semantics

Deriving a global flag via is_global_namespace and filtering accordingly matches the production code path. This helps prevent regressions.

lib/llm/tests/http_namespace_integration.rs (4)

34-102: Namespace filtering behavior tests are on point

Scenarios for specific namespace, global, and empty target are correctly validated against is_global_namespace. This aligns with the watcher’s filtering.


104-153: Endpoint parsing + global classification assertions add good coverage

Exercising both parse() and classification makes failures actionable if parsing or namespace utilities change.


155-222: Discovery scoping scenarios reflect expected visibility

The three scenarios cover the expected visible set under specific, global, and non-existent namespaces. Nice.


224-275: Boundary conditions are well-covered

Empty and “global” treated as global; uppercase “GLOBAL” intentionally not. This matches the current is_global_namespace contract.

Copy link
Contributor

@nnshah1 nnshah1 left a comment

Choose a reason for hiding this comment

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

LGTM

@biswapanda biswapanda force-pushed the bis/dyn-837-dynamo-namespace-scoping-for-frontend-component branch from ad534ec to 5b719a0 Compare September 2, 2025 22:36
@biswapanda biswapanda force-pushed the bis/dyn-838-dynamo-namespace-scoping-for-backend-component branch from 9ddc517 to cfd26d6 Compare September 2, 2025 23:23
@biswapanda biswapanda force-pushed the bis/dyn-838-dynamo-namespace-scoping-for-backend-component branch from cfd26d6 to c4200e4 Compare September 2, 2025 23:30
@biswapanda biswapanda force-pushed the bis/dyn-837-dynamo-namespace-scoping-for-frontend-component branch from 843912e to 75882f6 Compare September 2, 2025 23:31
@biswapanda biswapanda force-pushed the bis/dyn-837-dynamo-namespace-scoping-for-frontend-component branch 3 times, most recently from 16629b6 to bca639c Compare September 2, 2025 23:36
@biswapanda biswapanda force-pushed the bis/dyn-838-dynamo-namespace-scoping-for-backend-component branch from c4200e4 to 1abfbc0 Compare September 2, 2025 23:43
@biswapanda biswapanda force-pushed the bis/dyn-838-dynamo-namespace-scoping-for-backend-component branch from 1abfbc0 to 29c7d05 Compare September 3, 2025 00:42
@biswapanda biswapanda merged commit 1212436 into bis/dyn-837-dynamo-namespace-scoping-for-frontend-component Sep 3, 2025
7 of 8 checks passed
@biswapanda biswapanda deleted the bis/dyn-838-dynamo-namespace-scoping-for-backend-component branch September 3, 2025 01:32
biswapanda added a commit that referenced this pull request Sep 3, 2025
biswapanda added a commit that referenced this pull request Sep 3, 2025
biswapanda added a commit that referenced this pull request Sep 3, 2025
biswapanda added a commit that referenced this pull request Sep 3, 2025
biswapanda added a commit that referenced this pull request Sep 3, 2025
biswapanda added a commit that referenced this pull request Sep 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants