Skip to content

feat(jobs): add watch command to stream job and deployment events - #1031

Merged
ironcommit merged 1 commit into
mainfrom
jobs-watch-sdk-first/rsadler
Aug 25, 2026
Merged

feat(jobs): add watch command to stream job and deployment events#1031
ironcommit merged 1 commit into
mainfrom
jobs-watch-sdk-first/rsadler

Conversation

@ironcommit

@ironcommit ironcommit commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a nemo jobs watch CLI command and corresponding SDK watcher methods that stream job lifecycle events - status transitions, log lines, and warnings - directly to the terminal or programmatic consumers. The sync and async Jobs SDK clients both expose watch_job(...); the async client returns an async iterator backed by async_watch_job(...).

Changes

Core watch engine (jobs/watch.py)

  • Synchronous and async iterators that poll the Jobs API and yield typed JobWatchEvent objects (status, log, warning)
  • Deduplication of log lines across polls using cursor tracking
  • Configurable filtering by attempt_id, step_id, and task_id
  • Timeout enforcement with graceful completion on terminal job states
  • Transient-error retry on the next configured poll_interval

CLI command (nemo jobs watch)

  • Rich-rendered terminal output via the job watch renderer - color-coded status changes, streaming logs, and warning highlights
  • Options: --timeout, --poll-interval, --history/--no-history, --attempt-id, --step-id, --task-id, --limit, --workspace
  • Log streaming is enabled for CLI watch; include_logs=False is SDK-only
  • Non-zero exit code on job failure

--watch flag on create commands

  • nemo jobs create --watch and nemo inference deployments create --watch automatically watch after creation
  • Mutual exclusion with the existing --wait flag
  • Code generator updated to emit --watch support for configured create commands via cli_config.yaml

SDK client integration

  • client.jobs.watch_job(name, ...) -> sync iterator of JobWatchEvent
  • async_client.jobs.watch_job(name, ...) -> async iterator of JobWatchEvent
  • Plugin SDK clients wire watch into both sync and async NeMoPlatform clients through the Jobs resource

Waiters refactor

  • Renamed "Wait Options" panel to "Lifecycle Options" across all commands
  • Consolidated shared timeout/poll-interval options

Testing

  • Unit tests for the watch engine covering status transitions, log deduplication, timeout, retries, filtering, and pagination (test_watch.py)
  • CLI integration tests for nemo jobs watch and --watch on create commands (test_app.py, test_create_wait.py)
  • Renderer tests (test_job_watch_renderer.py)
  • Code generator tests for watch flag injection (test_code_generator.py, test_config.py)

Summary by CodeRabbit

  • New Features
    • Added jobs watch to stream job status, logs, warnings, and completion results.
    • Added --watch support for job and inference deployment creation, with timeout and polling controls.
    • Added filters for attempts, steps, tasks, logs, history, and pagination.
    • Added synchronous and asynchronous job monitoring support.
    • Added validation to prevent using --wait and --watch together.
  • Documentation
    • Updated CLI reference documentation with lifecycle options and watch command usage.
  • Bug Fixes
    • Improved handling of transient errors, timeouts, pagination, and terminal job states.

@ironcommit
ironcommit requested review from a team as code owners July 31, 2026 22:22
@github-actions github-actions Bot added the feat label Jul 31, 2026
@ironcommit
ironcommit requested review from mikeknep and parkanzky July 31, 2026 22:24
@github-actions

Copy link
Copy Markdown
Contributor

Comment thread tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py Dismissed
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔄 Running review...
📝 Walkthrough

Walkthrough

The PR adds synchronous and asynchronous job watching with typed status, log, and warning events. It integrates watch and wait modes into job and deployment creation, adds generated-command support, updates CLI rendering and validation, and documents the new lifecycle options.

Changes

Job Watch

Layer / File(s) Summary
Watch engine and client APIs
packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/*, packages/nemo_platform_plugin/src/nemo_platform_plugin/client/adapter.py, packages/nemo_platform_plugin/tests/jobs/test_watch.py
Adds typed events, synchronous and asynchronous watchers, paginated log handling, retries, cursor recovery, deduplication, and timeout enforcement.
Lifecycle configuration and generated create commands
packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py, tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/*, tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/*, packages/nemo_platform_ext/tests/cli/core/test_code_generator.py
Separates wait and watch configuration and generates lifecycle handling for platform jobs and inference deployments.
CLI watch commands and rendering
packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/*, packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/*, tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py, packages/nemo_platform_ext/tests/cli/*
Adds jobs watch, post-create watch modes, Rich event rendering, deployment verbosity controls, failure exits, and lifecycle validation.
CLI configuration and documentation
tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml, docs/cli/reference.mdx
Configures job and deployment watch behavior and documents the new commands and options.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI
  participant JobsClient
  participant watch_job
  participant PlatformAPI
  User->>CLI: Run jobs watch or create --watch
  CLI->>JobsClient: Start watch_job
  JobsClient->>watch_job: Pass filters, timeout, and polling
  watch_job->>PlatformAPI: Poll status and fetch logs
  PlatformAPI-->>watch_job: Status and log responses
  watch_job-->>JobsClient: Emit JobWatchEvent values
  JobsClient-->>CLI: Return event stream
  CLI-->>User: Render status, logs, warnings, and completion
Loading

Possibly related PRs

Suggested reviewers: mikeknep, parkanzky, mckornfield

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.30% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding job and deployment event streaming through watch commands and lifecycle support.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jobs-watch-sdk-first/rsadler

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

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py (1)

156-203: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix: string-replace corrupts generated code when the resource name contains --wait.

_render_lifecycle_code builds the prelude with the literal text --wait, then does prelude.replace("--wait", "--watch") for platform_job. This replace is not scoped to the error message. It also rewrites any occurrence of --wait inside the interpolated resource-name literal. If a job's name contains the substring --wait (for example nightly--wait-job), the generated code's quoted name literal is silently corrupted to nightly--watch-job.

Compute the flag name once from lifecycle_type and interpolate it directly instead of doing a blind substring replace after the fact.

🐛 Proposed fix
     resource_name = 'getattr(response, "name", None)'
     if args.get("name") is not None:
         resource_name = f"{resource_name} or {_format_python_literal(args['name'])}"

+    flag_name = "--watch" if lifecycle_type == "platform_job" else "--wait"
     prelude = dedent(
         f"""
         resource_name = {resource_name}
         if not resource_name:
-            raise RuntimeError("Unable to determine created resource name for --wait")
+            raise RuntimeError("Unable to determine created resource name for {flag_name}")
         """
     ).strip()

     if lifecycle_type == "inference_deployment":
         ...

     if lifecycle_type == "platform_job":
-        prelude = prelude.replace("--wait", "--watch")
         return "\n\n".join(
             [
                 prelude,
                 _render_platform_job_watch_code(args, timeout, poll_interval),
             ]
         )

     raise ValueError(f"Unsupported lifecycle config type: {lifecycle_type!r}")
🤖 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 `@packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py`
around lines 156 - 203, Update _render_lifecycle_code to compute the lifecycle
flag name from lifecycle_type before constructing prelude, using --watch for
platform_job and --wait otherwise, then interpolate that value into the error
message. Remove the prelude.replace("--wait", "--watch") call so resource-name
literals containing --wait remain unchanged.
tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2 (1)

32-41: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject methods that configure both wait and watch.

Both configurations are accepted independently. The template then emits duplicate timeout and poll_interval parameters, causing a Python SyntaxError.

🤖 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/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2`
around lines 32 - 41, Update the template conditions around the watch_config and
wait_config blocks so generated commands cannot enable both configurations
simultaneously. Preserve each configuration’s existing parameters when used
alone, and ensure timeout and poll_interval are emitted only once to avoid
duplicate function parameters.
🧹 Nitpick comments (1)
packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py (1)

199-228: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the function-local imports to module scope.

Line 28 already imports from nemo_platform_ext.jobs.watch at module level, so the deferred imports on Line 213 and Line 408 cannot break an import cycle. Import watch_job and async_watch_job normally.

Also add docstrings; every other public method on this client is documented.

♻️ Proposed change
-from nemo_platform_ext.jobs.watch import JobWatchEvent
+from nemo_platform_ext.jobs.watch import JobWatchEvent, async_watch_job, watch_job
     ) -> Iterator[JobWatchEvent]:
-        from nemo_platform_ext.jobs.watch import watch_job
-
         return watch_job(
As per coding guidelines: "prefer concrete type hints over string-based type hints, and do not import those types only under `TYPE_CHECKING`; import them normally when possible."
🤖 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 `@packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py` around
lines 199 - 228, Move watch_job and async_watch_job imports from their methods
to module scope alongside the existing nemo_platform_ext.jobs.watch import, then
remove the function-local imports. Add concise docstrings to the public
watch_job and async_watch_job client methods while preserving their current
delegation and parameters.

Source: Coding guidelines

🤖 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 `@packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py`:
- Around line 312-334: Update _drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py:312-334 to track
visited cursors, return when _next_page produces a cursor already seen or
otherwise fails to advance, and enforce the deadline on every loop iteration.
Apply the same cursor-advance guard and per-iteration deadline check in
_async_drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py:352-374.
- Around line 126-150: Update the polling loop around _drain_logs to persist and
reuse the latest next_page cursor across cycles instead of resetting to the
original page_cursor. When the server invalidates that cursor, fall back to the
existing full re-scan behavior; otherwise advance the cursor after each
successful drain while preserving terminal-status handling.
- Around line 126-146: Update the log-draining flow around _drain_logs so
history_seen becomes true immediately after the first page is recorded,
including when pagination later raises a transient error. Preserve suppression
of pre-existing history for include_history=False while allowing newly fetched
lines to emit on the retry, and remove reliance on setting history_seen only
after the entire drain succeeds.
- Around line 102-105: Make both watcher entry points validate eagerly by
converting watch_job at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py:102-105 and
async_watch_job at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py:168-171 into
non-generator wrappers. Keep the poll_interval checks and _sync_jobs_client or
_async_jobs_client resolution in each wrapper, then return inner generator or
async-generator functions containing the existing iteration logic.

---

Outside diff comments:
In `@packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py`:
- Around line 156-203: Update _render_lifecycle_code to compute the lifecycle
flag name from lifecycle_type before constructing prelude, using --watch for
platform_job and --wait otherwise, then interpolate that value into the error
message. Remove the prelude.replace("--wait", "--watch") call so resource-name
literals containing --wait remain unchanged.

In
`@tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2`:
- Around line 32-41: Update the template conditions around the watch_config and
wait_config blocks so generated commands cannot enable both configurations
simultaneously. Preserve each configuration’s existing parameters when used
alone, and ensure timeout and poll_interval are emitted only once to avoid
duplicate function parameters.

---

Nitpick comments:
In `@packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py`:
- Around line 199-228: Move watch_job and async_watch_job imports from their
methods to module scope alongside the existing nemo_platform_ext.jobs.watch
import, then remove the function-local imports. Add concise docstrings to the
public watch_job and async_watch_job client methods while preserving their
current delegation and parameters.
🪄 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: 75f56093-22d4-4101-9ce3-7b89b2a6a52a

📥 Commits

Reviewing files that changed from the base of the PR and between e9feac9 and e7a5748.

⛔ Files ignored due to path filters (11)
  • sdk/python/nemo-platform/src/nemo_platform/_client.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/jobs/watch.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_create_wait.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/test_app.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py is excluded by !sdk/**
📒 Files selected for processing (20)
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/tests/cli/core/test_code_generator.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/tests/jobs/test_watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py

Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py Outdated
Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py Outdated
Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py Outdated
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 35321/45084 78.3% 62.8%
Integration Tests 21239/42859 49.6% 22.7%

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from e7a5748 to a5ff80c Compare August 5, 2026 22:21
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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

🧹 Nitpick comments (1)
tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py (1)

923-1016: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a watch-only platform-job render test. No test covers a platform_job watch config without a wait config. That case is exactly where the template emits an unused wait_for_platform_job import (see the template comment).

🤖 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/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`
around lines 923 - 1016, Add a test alongside
test_create_platform_job_watch_config_renders_wait_and_watch covering a create
configuration with watch type platform_job but no wait block. Render the command
and assert watch-related options/imports and job-watch behavior are present
while the wait_for_platform_job import and wait-only rendering are absent,
matching the template’s watch-only output.
🤖 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/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2`:
- Around line 24-30: Update the conditional import for wait_for_platform_job in
the create command template so it is generated only when has_platform_job_wait
is enabled; keep the separate job-watch imports unchanged, since the watch-only
branch does not use wait_for_platform_job.

---

Nitpick comments:
In `@tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`:
- Around line 923-1016: Add a test alongside
test_create_platform_job_watch_config_renders_wait_and_watch covering a create
configuration with watch type platform_job but no wait block. Render the command
and assert watch-related options/imports and job-watch behavior are present
while the wait_for_platform_job import and wait-only rendering are absent,
matching the template’s watch-only output.
🪄 Autofix

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: 7a009706-6d46-4524-9341-85bd84fb0ee0

📥 Commits

Reviewing files that changed from the base of the PR and between 607acde and a5ff80c.

⛔ Files ignored due to path filters (15)
  • sdk/python/nemo-platform/src/nemo_platform/_client.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/inference/deployments/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/jobs/watch.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_create_wait.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/telemetry/test_job_events.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/test_app.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py is excluded by !sdk/**
📒 Files selected for processing (24)
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/tests/cli/core/test_code_generator.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • packages/nemo_platform_ext/tests/cli/core/test_waiters.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/tests/jobs/test_watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
🚧 Files skipped from review as they are similar to previous changes (10)
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from a5ff80c to 18140ca Compare August 5, 2026 22:38
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py (1)

794-811: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fixture annotations do not match the real watch_job contract.

watch_job returns Iterator[JobWatchEvent] and async_watch_job returns AsyncIterator[JobWatchEvent], not a bare JobWatchEvent. The fixture is a stand-in for enhanced.py, so a wrong annotation here weakens the vendor-rewrite test. It also leaves line 826 asserting that AsyncIterator and Iterator are imported while the fixture never uses either name, so that assertion no longer proves anything about the rewritten watch signatures.

Use the real return types, then assert each signature separately instead of count(...) == 2 on line 837.

♻️ Suggested fixture change
-    def watch_job(self, name: str) -> JobWatchEvent:
+    def watch_job(self, name: str) -> Iterator[JobWatchEvent]:
         \"\"\"Watch a job.\"\"\"

         return watch_job(self, name)
-    def watch_job(self, name: str) -> JobWatchEvent:
+    def watch_job(self, name: str) -> AsyncIterator[JobWatchEvent]:
         \"\"\"Watch a job asynchronously.\"\"\"

         return async_watch_job(self, name)

Add the matching import to the fixture source and update the assertions:

-    assert updated.count("def watch_job(self, name: str) -> JobWatchEvent:") == 2
+    assert "def watch_job(self, name: str) -> Iterator[JobWatchEvent]:" in updated
+    assert "def watch_job(self, name: str) -> AsyncIterator[JobWatchEvent]:" in updated
🤖 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/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py` around
lines 794 - 811, Update the fixture’s NeMoPlatform.watch_job and
AsyncNeMoPlatform.watch_job annotations to use Iterator[JobWatchEvent] and
AsyncIterator[JobWatchEvent], respectively, matching the real enhanced.py
contract. Add Iterator and AsyncIterator to the fixture imports, then replace
the combined import-count assertion with separate signature assertions for the
synchronous and asynchronous methods.
🧹 Nitpick comments (5)
packages/nemo_platform_ext/tests/cli/core/test_code_generator.py (2)

243-253: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

The label test does not assert what its name claims.

test_generate_python_code_with_platform_job_watch_ignores_label_formatting passes a label with quotes and {label}, then only checks compile and the --watch error string. Neither assertion proves the label was escaped or ignored. Assert that the raw label does not leak into the generated code.

💚 Suggested assertion
     compile(code, "<generated-code>", "exec")
     assert 'raise RuntimeError("Unable to determine created resource name for --watch")' in code
+    assert 'customization "job" {label}' not in code
🤖 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 `@packages/nemo_platform_ext/tests/cli/core/test_code_generator.py` around
lines 243 - 253, Update
test_generate_python_code_with_platform_job_watch_ignores_label_formatting to
assert that the raw watch_config resource_label, including its quotes and
{label} placeholder, is absent from the generated code. Keep the existing
compilation and RuntimeError assertions unchanged.

209-240: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for missing watch timeout validation.

_require_timeout validates watch mode, but the test covers only the wait error path. Add the proposed watch_options={"poll_interval": 10} test.

🤖 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 `@packages/nemo_platform_ext/tests/cli/core/test_code_generator.py` around
lines 209 - 240, Add a test beside
test_generate_python_code_with_inference_deployment_wait_requires_timeout
covering create generation with watch_config for inference_deployment and
watch_options containing only poll_interval. Assert generate_python_code raises
ValueError with the watch lifecycle timeout validation message, exercising
_require_timeout for watch mode.
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py (1)

393-426: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoff

seen_logs retains one entry per log line for the whole watch. Both drain functions share the same unbounded dedupe map. Now that the cursor advances between polls, only the recent page window needs dedupe coverage.

  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L393-L426: bound the seen_logs map in _drain_logs, for example by evicting keys behind the advanced cursor.
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L429-L477: apply the same bounding in _async_drain_logs.
🤖 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 `@packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py` around lines
393 - 426, Bound the shared seen_logs deduplication map to the active recent
page window instead of retaining every log key indefinitely. Update both
_drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L393-L426 and
_async_drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L429-L477 to
evict keys that fall behind the advanced page cursor while preserving
deduplication for logs still covered by the current window.
tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py (2)

963-964: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Tighten the timeout-default assertions.

assert "] = None" in rendered and assert "] = 1200" in rendered match any parameter default in the file, not the timeout parameter. They pass even if timeout loses its default. Assert the full annotated declaration instead.

Also applies to: 1004-1005

🤖 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/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`
around lines 963 - 964, In the timeout generation assertions around the existing
`rendered` checks, replace the broad substring checks for `] = None` and `] =
1200` with assertions matching the complete `timeout` annotated parameter
declaration and its expected default. Apply the same tightening to both affected
test cases so unrelated parameter defaults cannot satisfy the assertions.

700-709: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the Jinja environment setup between production and tests.

Extract the inline setup from SimpleGenerator.__init__ into a factory and use it in _render_create_command. The test currently uses a different to_kebab implementation than production.

🤖 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/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`
around lines 700 - 709, Extract the Jinja Environment configuration from
SimpleGenerator.__init__ into a shared factory, preserving the production
loader, options, and filters. Update _render_create_command to obtain its
environment through that factory and remove the duplicate inline setup so both
production and tests use the same to_kebab behavior.
🤖 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 `@packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py`:
- Around line 170-186: Cap retries for terminal-status log draining in both the
synchronous watcher loop at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L170-L186 and the
asynchronous watcher loop at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L271-L288. Track
failures from the final drain, emit a warning when the configured cap is
reached, and return instead of continuing indefinitely when timeout is None;
apply the same behavior consistently to both loops.

In `@packages/nemo_platform_ext/tests/jobs/test_watch.py`:
- Around line 484-488: Update the monotonic_values fixture in the watch_job
timeout test to provide a fallback value after the scripted 0.0, 0.0, and 10.0
readings, preventing unrelated calls to watch_module.time.monotonic from
exhausting the iterator. Preserve the existing timeout assertion and timing
sequence while ensuring additional calls do not raise StopIteration.

---

Outside diff comments:
In `@tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py`:
- Around line 794-811: Update the fixture’s NeMoPlatform.watch_job and
AsyncNeMoPlatform.watch_job annotations to use Iterator[JobWatchEvent] and
AsyncIterator[JobWatchEvent], respectively, matching the real enhanced.py
contract. Add Iterator and AsyncIterator to the fixture imports, then replace
the combined import-count assertion with separate signature assertions for the
synchronous and asynchronous methods.

---

Nitpick comments:
In `@packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py`:
- Around line 393-426: Bound the shared seen_logs deduplication map to the
active recent page window instead of retaining every log key indefinitely.
Update both _drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L393-L426 and
_async_drain_logs at
packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py#L429-L477 to
evict keys that fall behind the advanced page cursor while preserving
deduplication for logs still covered by the current window.

In `@packages/nemo_platform_ext/tests/cli/core/test_code_generator.py`:
- Around line 243-253: Update
test_generate_python_code_with_platform_job_watch_ignores_label_formatting to
assert that the raw watch_config resource_label, including its quotes and
{label} placeholder, is absent from the generated code. Keep the existing
compilation and RuntimeError assertions unchanged.
- Around line 209-240: Add a test beside
test_generate_python_code_with_inference_deployment_wait_requires_timeout
covering create generation with watch_config for inference_deployment and
watch_options containing only poll_interval. Assert generate_python_code raises
ValueError with the watch lifecycle timeout validation message, exercising
_require_timeout for watch mode.

In `@tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`:
- Around line 963-964: In the timeout generation assertions around the existing
`rendered` checks, replace the broad substring checks for `] = None` and `] =
1200` with assertions matching the complete `timeout` annotated parameter
declaration and its expected default. Apply the same tightening to both affected
test cases so unrelated parameter defaults cannot satisfy the assertions.
- Around line 700-709: Extract the Jinja Environment configuration from
SimpleGenerator.__init__ into a shared factory, preserving the production
loader, options, and filters. Update _render_create_command to obtain its
environment through that factory and remove the duplicate inline setup so both
production and tests use the same to_kebab behavior.
🪄 Autofix

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: 0127b6ea-560c-48e8-b381-796c88784c7b

📥 Commits

Reviewing files that changed from the base of the PR and between f608ebe and 18140ca.

⛔ Files ignored due to path filters (15)
  • sdk/python/nemo-platform/src/nemo_platform/_client.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/inference/deployments/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/jobs/watch.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_create_wait.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/telemetry/test_job_events.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/test_app.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py is excluded by !sdk/**
📒 Files selected for processing (24)
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/tests/cli/core/test_code_generator.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • packages/nemo_platform_ext/tests/cli/core/test_waiters.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/tests/jobs/test_watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
🚧 Files skipped from review as they are similar to previous changes (19)
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/init.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/init.py
  • packages/nemo_platform_ext/tests/cli/core/test_waiters.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py

Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py Outdated
Comment thread packages/nemo_platform_ext/tests/jobs/test_watch.py
@ironcommit ironcommit changed the title feat(jobs): add watch command to stream job events feat(jobs): add watch command to stream job and deployment events Aug 5, 2026
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from 18140ca to dfa04bc Compare August 5, 2026 23:34
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

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

🧹 Nitpick comments (2)
packages/nemo_platform_ext/tests/cli/core/test_code_generator.py (1)

243-253: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the escaping this test claims to verify.

The test name states label formatting is ignored, but the assertions only check compilation and the error string. A generator that interpolated the label unescaped into a docstring or comment could still pass. Add an assertion that the raw label is not injected outside a quoted literal.

♻️ Suggested assertion
     compile(code, "<generated-code>", "exec")
     assert 'raise RuntimeError("Unable to determine created resource name for --watch")' in code
+    assert 'customization "job" {label}' not in code
🤖 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 `@packages/nemo_platform_ext/tests/cli/core/test_code_generator.py` around
lines 243 - 253, Strengthen
test_generate_python_code_with_platform_job_watch_ignores_label_formatting by
asserting that the raw resource_label text is not injected into the generated
source outside a quoted literal, while retaining the existing compilation and
RuntimeError assertions.
tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py (1)

700-709: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the production Jinja environment in _render_create_command.

Extract the environment setup into a shared factory. The test currently replaces caseutil.to_kebab with a lambda that only replaces underscores, so it can render different command paths.

🤖 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/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`
around lines 700 - 709, Update _render_create_command to obtain its Jinja
Environment from the same shared production environment factory used by the CLI
generator, rather than constructing a test-specific environment with a local
to_kebab lambda. Preserve loading create_command.py.j2 and rendering with the
existing context, and remove the duplicated environment/filter setup.
🤖 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.

Nitpick comments:
In `@packages/nemo_platform_ext/tests/cli/core/test_code_generator.py`:
- Around line 243-253: Strengthen
test_generate_python_code_with_platform_job_watch_ignores_label_formatting by
asserting that the raw resource_label text is not injected into the generated
source outside a quoted literal, while retaining the existing compilation and
RuntimeError assertions.

In `@tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py`:
- Around line 700-709: Update _render_create_command to obtain its Jinja
Environment from the same shared production environment factory used by the CLI
generator, rather than constructing a test-specific environment with a local
to_kebab lambda. Preserve loading create_command.py.j2 and rendering with the
existing context, and remove the duplicated environment/filter setup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 050f3e33-c204-48d3-b41d-a24a883ee02b

📥 Commits

Reviewing files that changed from the base of the PR and between b3041f2 and dfa04bc.

⛔ Files ignored due to path filters (15)
  • sdk/python/nemo-platform/src/nemo_platform/_client.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/inference/deployments/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/commands/api/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/cli/core/waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/jobs/watch.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_create_wait.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_code_generator.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_job_watch_renderer.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/core/test_waiters.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/telemetry/test_job_events.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/test_app.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/__init__.py is excluded by !sdk/**
  • sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py is excluded by !sdk/**
📒 Files selected for processing (26)
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/__init__.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/tests/cli/core/test_code_generator.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • packages/nemo_platform_ext/tests/cli/core/test_waiters.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/tests/jobs/test_watch.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/adapter.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/client.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
🚧 Files skipped from review as they are similar to previous changes (21)
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • packages/nemo_platform_ext/tests/cli/core/test_waiters.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • docs/cli/reference.mdx
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/init.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/templates/create_command.py.j2
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/init.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/jobs/watch.py

Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/waiters.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py Outdated
@tylersbray

Copy link
Copy Markdown
Contributor

agent (follow-up-review): Remaining items after re-check of 58580df

Fresh open inline threads (not replies on resolved conversations):

Still open

  • R1 (P1) packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py:184verbose=watch silences plain --wait. Prefer default verbose for --wait (e.g. verbose=True when not watching).
  • R2 (P1) packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py:538 (+ async ~580) — don’t set history_seen=True until a full history drain completes when include_history=False.
  • R7 (P2, confirm) packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py:34paused in _FAILED_TERMINAL_STATUSES exits as failure; intentional vs neutral/successful terminal?

Fixed since prior review (no action)

R3 Live timer, R4 format_output, R5 error_details, R6 log errors non-fatal, R8 telemetry, R10 docs, R11 template guard, R13 backoff.

Optional / debt

  • R9 document poll pagination limitation on sync watch_job too
  • R12 sync/async duplication OK as follow-up

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

+1'ing Matt's approval so you are not blocked. Will be a nice feature to ship.

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from 58580df to cc46405 Compare August 7, 2026 01:03
@ironcommit

Copy link
Copy Markdown
Contributor Author

Addressed the human review feedback in cc46405:

  • R1: restored verbose deployment status output for plain --wait by generating/passing verbose=True.
  • R2: changed sync/async job log draining so include_history=False only marks the baseline seen after a full successful drain, preventing unread historical pages from leaking after mid-pagination failures.
  • R7: left paused as failure-terminal intentionally and added a code comment explaining that a paused job is resumable but incomplete, so wait/watch should not report it as done.
  • Also cleaned up the duplicated platform-job codegen import branch and documented pytest's importlib import mode choice.

Validation:

  • uv run --frozen pytest packages/nemo_platform_ext/tests/jobs/test_watch.py packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py packages/nemo_platform_ext/tests/cli/core/test_code_generator.py tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py -v
  • uv run --frozen pytest sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py::test_watch_job_suppresses_unread_history_after_partial_history_drain_failure sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/jobs/test_watch.py::test_async_watch_job_suppresses_unread_history_after_partial_history_drain_failure sdk/python/nemo-platform/tests/vendored/nemo_platform_ext/cli/commands/test_create_wait.py::test_inference_deployment_create_exits_when_wait_fails -v
  • uv run ruff check packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py packages/nemo_platform_ext/tests/jobs/test_watch.py packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • uv run ruff format --check packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/__init__.py packages/nemo_platform_ext/tests/jobs/test_watch.py packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_generator.py
  • uv run pre-commit run -a

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from cc46405 to 2784e4b Compare August 7, 2026 01:30
@ironcommit

Copy link
Copy Markdown
Contributor Author

Small correction to my previous validation comment: after a cleanup pass, the final commit 2784e4bfe removes the explicit generated verbose=... argument for inference deployment create wait/watch paths instead of passing verbose=True. That preserves the default progress output for plain --wait while avoiding the confusing verbose=watch/verbose=True plumbing in generated commands. Targeted command/generator and vendored mirror tests were rerun after this change.

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch 3 times, most recently from 984a97c to 674025a Compare August 7, 2026 14:59

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

left some nit comments, @ironcommit if they make sense I can add a commit for them

Comment thread sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py Outdated
Comment thread sdk/python/nemo-platform/src/nemo_platform/cli/core/job_watch_renderer.py Outdated
Comment thread sdk/python/nemo-platform/src/nemo_platform/cli/core/code_generator.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py Outdated
Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py Outdated
Comment thread docs/cli/reference.mdx Outdated

@parkanzky parkanzky 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 looking forward to this

@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch 3 times, most recently from 6bacd89 to adbd3f6 Compare August 24, 2026 22:22
@ironcommit
ironcommit added this pull request to the merge queue Aug 24, 2026
@ironcommit
ironcommit removed this pull request from the merge queue due to a manual request Aug 24, 2026
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from adbd3f6 to 864075f Compare August 24, 2026 23:50
@ironcommit
ironcommit added this pull request to the merge queue Aug 24, 2026
@ironcommit
ironcommit removed this pull request from the merge queue due to a manual request Aug 24, 2026
@ironcommit
ironcommit enabled auto-merge August 24, 2026 23:50
@ironcommit
ironcommit added this pull request to the merge queue Aug 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 25, 2026
@ironcommit
ironcommit added this pull request to the merge queue Aug 25, 2026
Merged via the queue into main with commit 9143480 Aug 25, 2026
60 checks passed
@ironcommit
ironcommit deleted the jobs-watch-sdk-first/rsadler branch August 25, 2026 00:51
ngoncharenko pushed a commit that referenced this pull request Aug 25, 2026
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
Signed-off-by: Nick Goncharenko <ngoncharenko@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants