Skip to content

Add MLflow tracking to modelopt MCP#1986

Open
ChenhanYu wants to merge 1 commit into
mainfrom
chenhany/omniml-5438-mcp-mlflow
Open

Add MLflow tracking to modelopt MCP#1986
ChenhanYu wants to merge 1 commit into
mainfrom
chenhany/omniml-5438-mcp-mlflow

Conversation

@ChenhanYu

@ChenhanYu ChenhanYu commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add optional MLflow run creation to modelopt-mcp submit_job
  • pass standard MLFLOW_RUN_ID and MLFLOW_TRACKING_URI into launched jobs
  • add MLflow finalization support to fetch terminal Nemo/Slurm logs with nemo experiment logs, upload launcher status/log artifacts, and record pass/fail
  • document the launcher workflow and add unit coverage

Testing

  • uv run --project tools/mcp pytest tools/mcp/tests/test_bridge.py
  • pre-commit hooks during commit: ruff, mypy, markdownlint, bandit, launcher YAML validation

Summary by CodeRabbit

  • New Features
    • Added optional MLflow tracking for experiment submissions.
    • Automatically records MLflow run metadata, status, metrics, and launcher/Nemo/Slurm logs.
    • Added the ability to finalize an existing MLflow run when an experiment reaches a terminal state.
    • Added configurable MLflow tracking URI, experiment, and run naming (with job name override).
  • Documentation
    • Updated MCP tool surface and example workflow to use MLflow-enabled submission and completion finalization.
  • Tests
    • Added MLflow coverage for Docker/Slurm flows and finalization behavior.

@copy-pr-bot

copy-pr-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

MLflow support is added to MCP job submission and experiment completion. The bridge creates runs, propagates identifiers, records submission failures, archives logs, and finalizes terminal status. Server tools, tests, dependency metadata, and documentation are updated.

MLflow Job Tracking

Layer / File(s) Summary
MLflow tool contracts and wiring
tools/mcp/modelopt_mcp/server.py, tools/mcp/pyproject.toml
MCP tools accept MLflow options, expose finalize_mlflow_run, and declare the MLflow dependency.
MLflow run lifecycle helpers
tools/mcp/modelopt_mcp/bridge.py
Bridge helpers resolve configuration, create runs, update failures, collect logs, fetch remote artifacts, and finalize runs.
MLflow-aware job submission
tools/mcp/modelopt_mcp/bridge.py, tools/mcp/tests/test_bridge.py
Submission creates MLflow metadata, injects launcher environment variables, returns run information, and handles Docker and Slurm failures with test coverage.
Terminal finalization and workflow validation
tools/mcp/modelopt_mcp/bridge.py, tools/mcp/tests/test_bridge.py, tools/mcp/README.md
Terminal waiting delegates finalization, log and status artifacts are validated, and documentation reflects the updated workflow and configuration.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: kevalmorabia97

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant submit_job
  participant MLflow
  participant Launcher
  participant wait_for_experiment
  participant finalize_mlflow_run
  MCPClient->>submit_job: enable_mlflow=True
  submit_job->>MLflow: create pre-submit run
  submit_job->>Launcher: pass MLflow identifiers
  Launcher-->>submit_job: experiment id and submission result
  MCPClient->>wait_for_experiment: finalize_mlflow=True
  wait_for_experiment->>finalize_mlflow_run: terminal experiment and run id
  finalize_mlflow_run->>MLflow: upload logs and record final status
Loading
🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding MLflow tracking to modelopt MCP.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Security Anti-Patterns ✅ Passed No banned patterns were added in the diff; the new mlflow dependency is Apache-2.0, so the license rule is satisfied. citeturn0search0turn0search2
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chenhany/omniml-5438-mcp-mlflow

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

@ChenhanYu
ChenhanYu force-pushed the chenhany/omniml-5438-mcp-mlflow branch from 5ed2f88 to 6f11a8e Compare July 17, 2026 04:04
@ChenhanYu ChenhanYu self-assigned this Jul 17, 2026
@ChenhanYu
ChenhanYu force-pushed the chenhany/omniml-5438-mcp-mlflow branch from 6f11a8e to 00b6ac8 Compare July 17, 2026 04:15
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.83%. Comparing base (92296ae) to head (d256b07).
⚠️ Report is 11 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1986      +/-   ##
==========================================
- Coverage   76.42%   72.83%   -3.60%     
==========================================
  Files         520      525       +5     
  Lines       58339    63093    +4754     
==========================================
+ Hits        44587    45951    +1364     
- Misses      13752    17142    +3390     
Flag Coverage Δ
unit 54.80% <ø> (-0.57%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChenhanYu
ChenhanYu force-pushed the chenhany/omniml-5438-mcp-mlflow branch from 00b6ac8 to e5a9c6e Compare July 17, 2026 04:33
@ChenhanYu
ChenhanYu marked this pull request as ready for review July 17, 2026 15:47
@ChenhanYu
ChenhanYu requested review from a team as code owners July 17, 2026 15:47
@ChenhanYu
ChenhanYu requested a review from kevalmorabia97 July 17, 2026 15:47
@ChenhanYu

Copy link
Copy Markdown
Collaborator Author

/claude review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Review Summary

Focused, well-structured PR adding optional MLflow tracking to the modelopt-mcp launcher bridge. Scope: 5 files (bridge.py, server.py, README.md, pyproject.toml, test_bridge.py). I reviewed all source files and the tests; skipped only the doc/pyproject text beyond a scan.

Findings — CRITICAL: 0, IMPORTANT: 1, SUGGESTION: 0

What's solid

  • Lazy import of mlflow via _setup_mlflow() correctly gates the optional dependency — users who don't enable tracking never import it, and a missing package returns a structured mlflow_not_installed result rather than raising.
  • Run lifecycle is coherent: pre-submit run creation, MLFLOW_RUN_ID/MLFLOW_TRACKING_URI injected into both Docker and Slurm child envs, terminal finalization uploads launcher logs and records a pass/fail result metric. The env injection sits after _apply_launcher_env, so it isn't clobbered.
  • Failure marking is applied on nearly every post-creation bail-out (Slurm FileNotFound/timeout/returncode/unparsed, Docker FileNotFound), so runs generally don't leak in the submitted state.
  • stdout redirect via contextlib.redirect_stdout keeps MLflow chatter out of the MCP stdio channel — important for an MCP server.
  • Test coverage is thorough, exercising env injection, run-name construction, artifact upload, Nemo-log fetch, and the wait_for_experiment delegation path.

Blocking-ish issue (1 IMPORTANT)

The Docker docker_submit_log_unavailable (OSError) early-return is the one post-run-creation failure path that omits _mark_mlflow_submission_failed. When enable_mlflow=True and the submit-log dir can't be created, the MLflow run is orphaned in lifecycle=submitted with no terminal result — the exact leak the other paths avoid. Inline comment has the one-line fix.

Risk assessment

Low risk. Tooling-only change (tools/mcp/), no modelopt core, no mode/state/export surface, feature is opt-in and defaults off. The single finding is a consistency gap on a rare error path, easily addressed.

🤖 Generated with Claude Code

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 6

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py`:
- Around line 799-833: Update the Nemo log-fetch loop around subprocess.run so
stdout and stderr stream directly to each target artifact instead of using
capture_output and duplicating the full body in memory. Open target for writing
before invoking the process, retain only a bounded stderr/output diagnostic tail
for error reporting, and preserve timeout, missing-CLI, and fetched-file
handling in the existing loop.
- Around line 779-787: Update _infer_nemo_log_task_count so an explicit_count is
only used when positive; treat zero or negative values as unknown and continue
to infer the count from status, falling back to 1 when the task_statuses map is
empty or unavailable. Preserve the existing nonnegative handling for positive
explicit counts and ensure callers passing 0 still fetch one terminal task log.
- Around line 1525-1544: Update the post-creation failure paths in the enclosing
submission flow so every failure after _create_mlflow_run succeeds calls
_mark_mlflow_submission_failed before returning. Apply this to both the Docker
log-creation failure path and the additional failure path around the referenced
lines, while preserving each existing response payload and handling only runs
that were successfully created.
- Around line 803-808: Remove the # nosec B603 B607 suppression from the
subprocess.run call in the bridge execution flow, then satisfy the Bandit check
by validating the argv contents before invocation or using the repository’s
approved subprocess abstraction. Preserve the fixed-argument, no-shell behavior
and existing timeout and output handling.
- Around line 874-907: Update the finalization flow before log fetching and
MLflow setup to reject any non-terminal status: after resolving final_status,
return an experiment_not_terminal failure for statuses other than done or
failed, including the experiment identifier and existing response fields as
appropriate. Allow the existing finalized lifecycle, result metric, and
log-upload path to run only for terminal statuses.
- Around line 848-864: Update finalize_mlflow_run_impl to validate experiment_id
through _validate_experiment_id before calling _resolve_experiment_dir or
performing any artifact/log upload; preserve the existing missing-mlflow-run
handling and ensure invalid external identifiers are rejected at this interface
boundary.
🪄 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: Enterprise

Run ID: 5fc75483-6a5f-4934-bbf1-5c490f3c354d

📥 Commits

Reviewing files that changed from the base of the PR and between bd1864d and e5a9c6e.

📒 Files selected for processing (5)
  • tools/mcp/README.md
  • tools/mcp/modelopt_mcp/bridge.py
  • tools/mcp/modelopt_mcp/server.py
  • tools/mcp/pyproject.toml
  • tools/mcp/tests/test_bridge.py

Comment thread tools/mcp/modelopt_mcp/bridge.py
Comment thread tools/mcp/modelopt_mcp/bridge.py Outdated
Comment thread tools/mcp/modelopt_mcp/bridge.py Outdated
Comment on lines +803 to +808
proc = subprocess.run( # nosec B603 B607 - fixed nemo CLI argv; no shell.
argv,
capture_output=True,
text=True,
timeout=120,
check=False,

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the prohibited Bandit bypass.

Line 803 adds # nosec B603 B607. Remove the suppression and address the check through validated arguments or the repository’s approved subprocess abstraction.

As per coding guidelines, “# nosec comments are not allowed as a bypass for security checks.”

🧰 Tools
🪛 ast-grep (0.44.1)

[error] 802-808: Command coming from incoming request
Context: subprocess.run( # nosec B603 B607 - fixed nemo CLI argv; no shell.
argv,
capture_output=True,
text=True,
timeout=120,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 803 - 808, Remove the # nosec
B603 B607 suppression from the subprocess.run call in the bridge execution flow,
then satisfy the Bandit check by validating the argv contents before invocation
or using the repository’s approved subprocess abstraction. Preserve the
fixed-argument, no-shell behavior and existing timeout and output handling.

Source: Coding guidelines

Comment thread tools/mcp/modelopt_mcp/bridge.py
Comment thread tools/mcp/modelopt_mcp/bridge.py
Comment thread tools/mcp/modelopt_mcp/bridge.py
@ChenhanYu

Copy link
Copy Markdown
Collaborator Author

/ok to test e5a9c6e

Signed-off-by: Chenhan Yu <chenhany@nvidia.com>
@ChenhanYu
ChenhanYu force-pushed the chenhany/omniml-5438-mcp-mlflow branch from e5a9c6e to d256b07 Compare July 18, 2026 01:40

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 4

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py`:
- Around line 895-901: Update the MLflow finalization loop around
_infer_nemo_log_task_count and _fetch_single_nemo_log_to_file to enforce one
overall deadline for the operation rather than allowing each sequential fetch
its own 120-second timeout. Use bounded concurrency or stop scheduling work when
the deadline is exhausted, while preserving per-task log retrieval and result
handling within the available budget.
- Around line 813-824: Update the parent result-handling logic around
result_queue so an absent child message is treated as failure, not success: do
not default result.get("ok", True) to true, and require an explicit successful
result before archiving. Preserve the existing error reporting for messages with
ok set to false.
- Around line 762-778: Harden _mlflow_artifact_files and the related
finalization log-capture/upload paths so job-created symlinks are never
followed: open log targets with no-follow semantics, reject symlinks and
non-regular files, and upload only securely snapshotted regular files whose
resolved location remains within exp_dir. Apply the same validation to the
referenced log-writing and MLflow upload call sites, preserving the existing
artifact patterns and deduplication behavior.
- Around line 699-720: The failure path in _mark_mlflow_submission_failed() must
explicitly terminate the reopened MLflow run as FAILED instead of allowing the
start_run context to finish as FINISHED. Update that method to use
MlflowClient.set_terminated with status="FAILED" or call
mlflow.end_run(status="FAILED") while preserving the existing failure-recording
behavior.
🪄 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: Enterprise

Run ID: 8547b75b-3097-4d1c-8bc2-c98747aa9757

📥 Commits

Reviewing files that changed from the base of the PR and between e5a9c6e and d256b07.

📒 Files selected for processing (5)
  • tools/mcp/README.md
  • tools/mcp/modelopt_mcp/bridge.py
  • tools/mcp/modelopt_mcp/server.py
  • tools/mcp/pyproject.toml
  • tools/mcp/tests/test_bridge.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tools/mcp/pyproject.toml
  • tools/mcp/README.md
  • tools/mcp/modelopt_mcp/server.py

Comment on lines +699 to +720
with contextlib.redirect_stdout(stdout):
mlflow.set_tracking_uri(uri)
mlflow.set_experiment(exp_name)
with mlflow.start_run(run_name=run_name) as run:
run_id = run.info.run_id
experiment_id = run.info.experiment_id
mlflow.log_params(
{
"target_yaml": yaml_path,
"executor": executor,
"cluster_host": cluster_host or "",
"source_ref": source.ref if source else "",
"source_sha": source.resolved_sha if source else "",
}
)
mlflow.set_tags(
{
"lifecycle": "submitted",
"managed_by": "modelopt-mcp",
}
)
except Exception as exc: # pragma: no cover - exercised through unit-level monkeypatches.

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 '\b(start_run|create_run|set_terminated|end_run)\s*\(' \
  tools/mcp/modelopt_mcp/bridge.py tools/mcp/tests/test_bridge.py
rg -n -C2 'mlflow' tools/mcp/pyproject.toml

Repository: NVIDIA/Model-Optimizer

Length of output: 3518


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('tools/mcp/modelopt_mcp/bridge.py')
lines = p.read_text().splitlines()
for start,end in [(660,760),(940,1010)]:
    print(f"\n--- {p} lines {start}-{end} ---")
    for i in range(start-1, min(end, len(lines))):
        print(f"{i+1:4d}: {lines[i]}")
PY

printf '\n--- AST outline ---\n'
ast-grep outline tools/mcp/modelopt_mcp/bridge.py --view expanded

Repository: NVIDIA/Model-Optimizer

Length of output: 12123


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('tools/mcp/tests/test_bridge.py')
lines = p.read_text().splitlines()
for start,end in [(760,860),(1540,1665)]:
    print(f"\n--- {p} lines {start}-{end} ---")
    for i in range(start-1, min(end, len(lines))):
        print(f"{i+1:4d}: {lines[i]}")
PY

Repository: NVIDIA/Model-Optimizer

Length of output: 9210


🌐 Web query:

MLflow start_run context manager status FINISHED on exit run_id resume run create_run set_terminated documentation

💡 Result:

In MLflow, the mlflow.start_run function serves as the primary entry point for managing experiment runs, providing a context manager interface to ensure runs are automatically terminated [1][2][3]. Context Manager Behavior: When used as a context manager (e.g., with mlflow.start_run:), the run remains active throughout the duration of the code block [1]. Upon exiting the block—whether successfully or due to an exception—the run is automatically closed [1][4]. The automatic termination behavior defaults to a status of FINISHED if no error occurs, though the exact status logic is managed internally by the run's lifecycle [1][5][3]. Resuming a Run: You can resume an existing run by passing its unique run_id to mlflow.start_run(run_id=...) [6][7][8]. When a run is resumed, MLflow sets its status to RUNNING [7][8]. If you are experiencing issues where a resumed run does not properly transition to a running state, it is common to use the MlflowClient to explicitly update the status [9][6][10]. Manual Run Management: If you need finer control over the run lifecycle, you can use the MLflow Fluent API or the MlflowClient: - mlflow.end_run(status='FINISHED'): Manually terminates the currently active run with the specified status (e.g., FINISHED, FAILED, or KILLED) [2][5][3]. - MlflowClient.create_run(...): Creates a new run object without automatically setting it as the active run, which is useful when you want to manage runs without the implicit context manager behavior [10]. - MlflowClient.set_terminated(run_id, status='FINISHED'): Explicitly updates an existing run's status to a terminal state [10]. For complex scenarios where standard context management is insufficient, the MlflowClient provides the most robust methods for programmatically creating, updating, and terminating runs [10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C3 '_mark_mlflow_submission_failed|_create_mlflow_run\(' tools/mcp/modelopt_mcp/bridge.py

Repository: NVIDIA/Model-Optimizer

Length of output: 2758


Mark failed submissions as FAILED.
_mark_mlflow_submission_failed() reopens the run with start_run(run_id=...), but that context still exits as FINISHED, so launcher failures are recorded as successful runs. Use MlflowClient.set_terminated(..., status="FAILED") or mlflow.end_run(status="FAILED") here.

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 699 - 720, The failure path in
_mark_mlflow_submission_failed() must explicitly terminate the reopened MLflow
run as FAILED instead of allowing the start_run context to finish as FINISHED.
Update that method to use MlflowClient.set_terminated with status="FAILED" or
call mlflow.end_run(status="FAILED") while preserving the existing
failure-recording behavior.

Comment on lines +762 to +778
def _mlflow_artifact_files(exp_dir: Path) -> list[Path]:
"""Local launcher files worth preserving durably in MLflow."""
patterns = [
"log_*.out",
"nemo_logs_task_*.log",
"status_*.out",
"_DONE",
_SLURM_STATUS_META,
]
seen: set[Path] = set()
files: list[Path] = []
for pattern in patterns:
for path in sorted(exp_dir.glob(pattern)):
if path.is_file() and path not in seen:
seen.add(path)
files.append(path)
return files

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.

🔒 Security & Privacy | 🔴 Critical | 🏗️ Heavy lift

Do not follow job-controlled symlinks during log capture or upload.

A workload can pre-create nemo_logs_task_0.log or log_*.out as a symlink. Finalization then either truncates a host file via open("w") or uploads an arbitrary readable host file to MLflow. Open targets with no-follow semantics and upload only securely snapshotted regular files contained within exp_dir.

Also applies to: 807-812, 895-900, 983-985

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 762 - 778, Harden
_mlflow_artifact_files and the related finalization log-capture/upload paths so
job-created symlinks are never followed: open log targets with no-follow
semantics, reject symlinks and non-regular files, and upload only securely
snapshotted regular files whose resolved location remains within exp_dir. Apply
the same validation to the referenced log-writing and MLflow upload call sites,
preserving the existing artifact patterns and deduplication behavior.

Comment on lines +813 to +824
except BaseException as exc:
with contextlib.suppress(Exception):
result_queue.put(
{
"ok": False,
"error_type": type(exc).__name__,
"diagnostic": str(exc),
}
)
return
else:
result_queue.put({"ok": True})

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not treat a missing child result as success.

The child catches failures and exits with code 0; if its queue message is unavailable, result.get("ok", True) silently reports success and archives a partial file.

Proposed fix
     try:
-        result = result_queue.get_nowait()
+        result = result_queue.get(timeout=1)
     except queue.Empty:
-        result = {}
-    if proc.exitcode == 0 and result.get("ok", True):
+        return {
+            "ok": False,
+            "reason": "logs_fetch_failed",
+            "diagnostic": "Nemo log fetch exited without returning a result.",
+            "exit_code": proc.exitcode,
+        }
+    if proc.exitcode == 0 and result.get("ok") is True:
         return {"ok": True}

Also applies to: 861-866

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 813 - 824, Update the parent
result-handling logic around result_queue so an absent child message is treated
as failure, not success: do not default result.get("ok", True) to true, and
require an explicit successful result before archiving. Preserve the existing
error reporting for messages with ok set to false.

Comment on lines +895 to +901
for job_idx in range(_infer_nemo_log_task_count(experiment_id, task_count)):
target = exp_dir / f"nemo_logs_task_{job_idx}.log"
result = _fetch_single_nemo_log_to_file(
experiment_id=experiment_id,
job_idx=job_idx,
target=target,
)

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Bound total MLflow finalization time.

Sequential 120-second fetches make worst-case latency task_count × 120s; a terminal job with many tasks can block the MCP request for hours. Enforce an overall deadline and use bounded concurrency or stop once the budget is exhausted.

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 895 - 901, Update the MLflow
finalization loop around _infer_nemo_log_task_count and
_fetch_single_nemo_log_to_file to enforce one overall deadline for the operation
rather than allowing each sequential fetch its own 120-second timeout. Use
bounded concurrency or stop scheduling work when the deadline is exhausted,
while preserving per-task log retrieval and result handling within the available
budget.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant