Skip to content

fix(frontend): use --model-path metadata instead of worker MDC paths - #9110

Closed
Pernekhan wants to merge 2 commits into
ai-dynamo:mainfrom
Pernekhan:pernekhan/fix-frontend-worker-model-path
Closed

fix(frontend): use --model-path metadata instead of worker MDC paths#9110
Pernekhan wants to merge 2 commits into
ai-dynamo:mainfrom
Pernekhan:pernekhan/fix-frontend-worker-model-path

Conversation

@Pernekhan

@Pernekhan Pernekhan commented May 4, 2026

Copy link
Copy Markdown

Overview:

We deploy the frontend and workers on separate hosts. Workers hold the engine weights at one path (say /data/<model> on the GPU node), and the frontend only needs the small metadata files (config, tokenizer, chat template) at some path on the CPU node. Those two paths often don't match.

Today the frontend just trusts the worker's MDC paths. If they don't resolve locally, it tries to grab the files from HuggingFace using the worker's local path as a repo id — which obviously can't work. There's no way to tell the frontend "use my own files instead."

This PR adds that. Pass --model-path to the frontend and its files take precedence over what the worker advertises. The worker still owns everything else (kv cache size, runtime config, etc.).

Details:

In do_worker_set_registration, before download_config, we replace the worker MDC's metadata-file slots (model_info, tokenizer, prompt_formatter, chat_template_file, gen_config) with whatever the frontend's --model-path loaded. Without --model-path, the watcher gets None and behaves exactly like main.

Internal API:

  • ModelDeploymentCard::override_files_with(&mut self, other: &Self)
  • ModelWatcher::set_local_card(Option<ModelDeploymentCard>)

3 unit tests in lib/llm/tests/model_card.rs. Existing tests still pass. 5 files changed, +80 / −1.

There's a bigger plan in DEP #8749 (workers self-host metadata over HTTP). This PR is a smaller, complementary change for deployments that can already mount the metadata files into the frontend pod.

Where should the reviewer start?

  • lib/llm/src/model_card.rsoverride_files_with, five lines.
  • lib/llm/src/discovery/watcher.rs — the call site in do_worker_set_registration, just before download_config.
  • lib/llm/src/entrypoint/input/http.rs and …/grpc.rs — same one-liner deciding whether to forward the frontend's card.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features

    • Added ability to override model configuration during discovery across gRPC and HTTP endpoints.
  • Tests

    • Added test coverage for model configuration override behavior.

@copy-pr-bot

copy-pr-bot Bot commented May 4, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

👋 Hi Pernekhan! Thank you for contributing to ai-dynamo/dynamo.

Just a reminder: The NVIDIA Test Github Validation CI runs an essential subset of the testing framework to quickly catch errors.Your PR reviewers may elect to test the changes comprehensively before approving your changes.

🚀

@github-actions github-actions Bot added the external-contribution Pull request is from an external contributor label May 4, 2026
@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch 2 times, most recently from 477cd01 to ae0ea91 Compare May 4, 2026 22:41
@pull-request-size pull-request-size Bot added size/M and removed size/L labels May 4, 2026
@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch 3 times, most recently from f488286 to 88c3234 Compare May 5, 2026 18:20
@Pernekhan
Pernekhan marked this pull request as ready for review May 5, 2026 18:21
@Pernekhan
Pernekhan requested a review from a team May 5, 2026 18:21
Comment thread lib/llm/src/discovery/watcher.rs Outdated
@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The PR adds support for per-watcher local card overrides in the model discovery system. ModelWatcher gains an optional local_card field that callers can install via a public setter. During worker set registration, discovered cards are merged with the local override using a new override_files_with method before config download. Both gRPC and HTTP entry points compute and inject local cards during setup.

Changes

Model Card Override & Watcher Integration

Layer / File(s) Summary
Data Shape
lib/llm/src/discovery/watcher.rs
ModelWatcher adds private local_card: Option<ModelDeploymentCard> field initialized to None.
Card Override Logic
lib/llm/src/model_card.rs
New ModelDeploymentCard::override_files_with(&mut self, other: &ModelDeploymentCard) method clones model_info, tokenizer, prompt_formatter, chat_template_file, and gen_config from the source card.
Watcher Setter & Registration
lib/llm/src/discovery/watcher.rs
Public set_local_card setter added; do_worker_set_registration applies the override via card.override_files_with(local) before config download when local_card is present.
Entry Point Wiring
lib/llm/src/entrypoint/input/grpc.rs, lib/llm/src/entrypoint/input/http.rs
Both gRPC and HTTP setup paths compute local_card from the model, extend run_watcher signature to accept it, and call set_local_card on the watcher before starting discovery.
Tests & Validation
lib/llm/tests/model_card.rs
Three new test cases verify field replacement, preservation of runtime fields during override, and clearing of slots when source provides no required files.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title mentions 'frontend' and '--model-path metadata' but the changes primarily involve backend watcher/discovery logic, not UI frontend code. Revise the title to reflect the actual changes, e.g., 'fix(watcher): allow frontend --model-path to override worker metadata' or 'feat(discovery): support local card override in model watcher'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description covers all required template sections with clear explanations of the problem, solution, affected files, and related issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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: 1

🤖 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 `@lib/llm/src/model_card.rs`:
- Around line 601-609: override_files_with currently updates
model_info/tokenizer/prompt_formatter/chat_template_file/gen_config but does not
clear the cached checksum used by mdcsum(), causing stale checksum reads; after
cloning those fields in override_files_with, reset the OnceLock that caches the
mdcsum (the cached checksum field used by mdcsum, e.g. mdc_sum or similar) so
subsequent calls to mdcsum() recompute the checksum; ensure you reference the
cached checksum field by its actual name and clear or reinitialize it inside
override_files_with immediately after the clones.
🪄 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: b189791e-cd25-4ae5-ba37-71cbae2edfe3

📥 Commits

Reviewing files that changed from the base of the PR and between e06a3d3 and 88c3234.

📒 Files selected for processing (5)
  • lib/llm/src/discovery/watcher.rs
  • lib/llm/src/entrypoint/input/grpc.rs
  • lib/llm/src/entrypoint/input/http.rs
  • lib/llm/src/model_card.rs
  • lib/llm/tests/model_card.rs

Comment thread lib/llm/src/model_card.rs
@rmccorm4
rmccorm4 requested review from nnshah1 and tanmayv25 May 5, 2026 21:15
@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch from 88c3234 to 67cba1c Compare May 5, 2026 22:05
@pull-request-size pull-request-size Bot added size/L and removed size/M labels May 5, 2026
@Pernekhan

Copy link
Copy Markdown
Author

Both review comments addressed in 67cba1c:

  1. @dynamo-ops — moved override_files_with to the very top of handle_put, before any mdcsum/save_model_card call. Both branches (existing-worker-set, concurrent-recovery, first-time registration) now see the overridden card consistently. Workers joining an existing WorkerSet compare and store with the overridden checksum.

  2. @coderabbitaioverride_files_with now resets self.checksum = OnceLock::new(). Five of the seven mdcsum inputs are mutated by the override, so the cache must be invalidated. New unit test test_override_files_with_invalidates_mdcsum_cache pins this.

Local checks still green: cargo build/clippy/fmt clean, cargo test --test model_card 11/11, cargo test --test http-service 4/4 (regression).

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@Pernekhan Thank you for the thorough follow-up! The fix looks well-reasoned — resetting self.checksum = OnceLock::new() in override_files_with is the correct approach given that five of the seven mdcsum inputs are mutated, and backing it with test_override_files_with_invalidates_mdcsum_cache ensures the invariant is locked in for future refactors. Great work!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch 2 times, most recently from 8236247 to ec710a7 Compare May 5, 2026 22:14
@Pernekhan

Copy link
Copy Markdown
Author

Pushed ec710a7: gated the override on display_name match.

Caught a multi-model edge case: if the frontend was launched with --model-path for one model and a worker registers a different model, the previous version of this PR would have clobbered the new model's tokenizer/config with the frontend's. With the gate local.display_name == card.display_name, the override is now scoped to the model the frontend was launched for — workers for any other model flow through unchanged like upstream main.

Cases:

  • Frontend --model-name X --model-path …, worker registers X → override applies.
  • Frontend --model-name X --model-path …, worker registers Y → skipped, Y handled like today.
  • Frontend with no --model-pathlocal_card is None, no override.

Local checks still green: cargo build/clippy/fmt clean, 11/11 model_card tests, 4/4 http-service regression.

@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch 3 times, most recently from 204374b to 312f07c Compare May 5, 2026 23:16
@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch from 312f07c to bf15c5a Compare May 5, 2026 23:20
When a worker publishes its MDC to discovery, the file paths inside it
(tokenizer.json, config.json, etc.) point at the worker's local
filesystem (e.g. /worker/weights). On the frontend node these paths
typically don't exist, so download_config falls through to a HuggingFace
Hub fetch using the worker's local path as if it were a repo id —
producing a 401 because /worker/weights is not a valid HF id.

Fix: when the frontend was started with --model-path /frontend/config,
treat that LocalModel card as a metadata donor. The discovery watcher
swaps the worker MDC's metadata-file slots (model_info, tokenizer,
prompt_formatter, chat_template_file, gen_config) with the donor's
references before download_config runs. download_config then takes the
already-local fast path.

This supports deployments where the worker has engine weights at one
path and the frontend has a metadata-only directory (no weights) at a
different path on a different filesystem. No new CLI flag — if
--model-path was not passed, behavior is unchanged.

Slot replacement is unconditional (no checksum gating). The frontend's
--model-path is treated as the source of truth for tokenizer/config; the
files at the two locations are expected to differ in general (e.g.
weights present on worker, absent on frontend).

Wired through both http.rs and grpc.rs run_watcher entry points.

4 new unit tests in lib/llm/tests/model_card.rs cover has_metadata_files
and adopt_metadata_from (full slot replacement, runtime-field
preservation, absent donor slots are skipped).

Signed-off-by: Pernekhan Utemuratov <pernekhan@deepinfra.com>
@Pernekhan
Pernekhan force-pushed the pernekhan/fix-frontend-worker-model-path branch from bf15c5a to 50881fe Compare May 5, 2026 23:23
@Pernekhan
Pernekhan requested a review from dynamo-ops May 7, 2026 03:15
@nnshah1

nnshah1 commented May 7, 2026

Copy link
Copy Markdown
Contributor

Heads-up — PR #9057 (frontend MDC verify-and-cache pipeline) is landing the same --model-path plumbing as part of a broader resolve+blake3+content-addressed-cache flow. After both merge I'll consolidate so we end up with a single integration point. Behavior preserved for your deployment, no action needed here.

What the consolidation looks like: local_card and override_files_with collapse into a single local_model_path: Option<PathBuf> on ModelWatcher. The override is applied at URI synthesis time (in checked_file_uri) instead of by swapping CheckedFile slots wholesale, which means worker-published checksums stay authoritative and any drift surfaces via blake3 verification rather than getting silently masked.

Final fallback chain in checked_file_uri (per CheckedFile slot, applied after coercing path-only slots to a synthetic file:// URL so the logic is uniform):

  1. cf.url() is http://, https://, or hf:// → use as-is. Worker self-host and HF Hub always win; --model-path never overrides them.
  2. cf.url() is file:// and the file exists on the frontend host → use as-is. Shared-mount deployments keep working.
  3. cf.url() is file://, the file is missing, AND --model-path/<basename> exists → rewrite to file://<--model-path>/<basename>. This is the rung that fires for your deployment: the worker's path string isn't reachable on the frontend, but the operator's --model-path directory has the same files.
  4. Otherwise → fall back to hf://<source_path>/<basename> (existing legacy behavior).

What this means for your deployment: rung 3 fires, the operator's --model-path provides the bytes, those bytes get blake3-verified against the worker-published checksum, and the verified copy lands in ~/.cache/dynamo/mdc/. The verification step is the one functional difference from this PR's override_files_with — but in your topology you're running the same files on both sides, so checksums match and the verification is a no-op. If they ever didn't match, that'd be real drift the operator should know about.

Multi-model robustness: because rung 3 only fires when --model-path/<basename> actually exists, a frontend that discovers other models via DRT discovery (whose files aren't in --model-path) falls through to rung 4 (hf://) for those models instead of erroring. With override_files_with's wholesale swap, every discovered model would get the local card's checksums regardless.

Plumbing diff at consolidation time: drop local_card field + set_local_card setter on ModelWatcher, drop ModelDeploymentCard::override_files_with, drop the if local.display_name == card.display_name hook in do_worker_set_registration. Replace with local_model_path: Option<PathBuf> field + set_local_model_path setter (same shape, simpler payload), threaded through download_config(local_model_path). The entrypoint/input/{http,grpc}.rs plumbing keeps the same source — (!model.path().as_os_str().is_empty()).then(|| model.path().to_path_buf()) — just hands the watcher a PathBuf instead of a whole ModelDeploymentCard.

Happy to loop you in on the consolidation when it goes up — if any of the rungs above don't match what your deployment needs, we can adjust. PR #9057: #9057

@nnshah1

nnshah1 commented May 7, 2026

Copy link
Copy Markdown
Contributor

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented May 7, 2026

Copy link
Copy Markdown

/ok to test

@nnshah1, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

nnshah1 added a commit that referenced this pull request May 7, 2026
Threads the frontend's --model-path through ModelWatcher into
checked_file_uri's fallback chain. When set, file:// (or path-only)
CheckedFile slots whose worker-published location is unreachable on
the frontend host fall back to <--model-path>/<basename> before
hf://<source_path>/<basename>. Worker-published checksums stay
authoritative; the override only chooses the byte source, not the
expected hash, so any drift surfaces via blake3 verification.

Coexists with PR #9110 (DeepInfra's wholesale-slot override): on merge,
this URI-synthesis-time override supersedes override_files_with.
Resolution chain in checked_file_uri:

  1. http(s):// or hf://  -> use as-is
  2. file://  AND original file exists  -> use as-is
  3. file://  AND original missing AND <--model-path>/<basename> exists
     -> rewrite to file://<--model-path>/<basename>
  4. otherwise  -> hf://<source_path>/<basename>

Path-only CheckedFiles are coerced to a synthetic file:// URL up
front so the four rungs apply uniformly.

9 unit tests cover all four rungs across path-only and url=file://
shapes. download_config gains an Option<&Path> parameter; ModelWatcher
gains local_model_path field + set_local_model_path setter; entrypoints
forward LocalModel.path() when --model-path was supplied.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: nnshah1 <neelays@nvidia.com>

@nnshah1 nnshah1 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.

LGTM — happy to land this now. Consolidation onto the URI-synthesis-time override in #9057 will be a mechanical follow-up; details in the explainer comment above.

@nnshah1

nnshah1 commented May 7, 2026

Copy link
Copy Markdown
Contributor

/ok to test 50881fe

@nnshah1

nnshah1 commented May 7, 2026

Copy link
Copy Markdown
Contributor

/ok to test 50881fe

@nnshah1
nnshah1 enabled auto-merge (squash) May 11, 2026 18:33
@nnshah1

nnshah1 commented May 12, 2026

Copy link
Copy Markdown
Contributor

Heads-up — PR #9057 just merged (squash c4f214d77e) and includes the --model-path plumbing as part of a broader frontend MDC verify-and-cache pipeline. Your fix is now subsumed by that change: when the frontend passes --model-path, the resolve pipeline falls back to it for any worker-side file:// path that isn't reachable on the frontend host, then blake3-verifies the local copy against the worker's MDC before serving it. The 4-rung chain handles HTTP/file/hf URIs uniformly, so the DeepInfra topology works regardless of whether self-hosting metadata is enabled on the worker.

Marking this as draft for now since the capability is upstream — feel free to close it, or repurpose it for any follow-up gaps (e.g. multi-model display-name routing) you'd like to revisit. Thanks again for the contribution and for pushing on this case — the discussion here directly shaped the 4-rung design.

@nnshah1
nnshah1 marked this pull request as draft May 12, 2026 21:34
auto-merge was automatically disabled May 12, 2026 21:34

Pull request was converted to draft

@nnshah1

nnshah1 commented May 12, 2026

Copy link
Copy Markdown
Contributor

And to be clear — really appreciate you flagging the DeepInfra deployment topology and contributing the fix. That use case (frontend host has the model files at a different path than the worker advertises) wasn't on our radar, and your PR is what surfaced it. The merged version generalized the approach, but the feature is yours — thanks for pushing on this. 🙏

@Pernekhan Pernekhan closed this May 13, 2026
@Pernekhan

Copy link
Copy Markdown
Author

#9057 addresses what we wanted. Thanks for quickly shipping it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contribution Pull request is from an external contributor fix size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants