Skip to content

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

Open
ironcommit wants to merge 2 commits into
mainfrom
jobs-watch-sdk-first/rsadler
Open

feat(jobs): add watch command to stream job and deployment events#1031
ironcommit wants to merge 2 commits 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 31999/40646 78.7% 63.3%
Integration Tests 18506/38598 47.9% 20.5%

@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
Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/client/enhanced.py Outdated
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py Outdated
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from dfa04bc to 9fe6249 Compare August 6, 2026 01:13
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 3

Caution

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

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

814-826: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Seed stale symbols in the target fixture.

The fixture does not contain AsyncIterator, Iterator, jobs.watch, or def watch_job. These assertions pass if stale cleanup does nothing. Add stale imports and methods to client_path before invoking _replace_client_methods.

🤖 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 814 - 826, Update the fixture setup for client_path before invoking
_replace_client_methods to add stale AsyncIterator and Iterator imports plus a
watch_job method and jobs.watch reference. Keep the existing assertions
unchanged so the test verifies these stale symbols are actually removed rather
than passing when no cleanup is needed.
tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py (1)

2120-2125: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Run stale cleanup when no replacement methods exist.

When collector.class_methods is empty, return at line 2118 skips _ClientMethodReplacer and _ClientStaleImportRemover. Continue with empty replacements so stale watch_job methods and imports are removed.

🤖 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/vendor/vendor_package.py`
around lines 2120 - 2125, Update the control flow around _ClientMethodReplacer
so an empty collector.class_methods collection does not return before stale
cleanup. Always visit the target tree with _ClientMethodReplacer and then
_ClientStaleImportRemover, allowing stale watch_job methods and imports to be
removed even when no replacement methods exist.
🧹 Nitpick comments (1)
packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py (1)

101-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Drop the quoted return annotation.

from __future__ import annotations is active on line 4, so the forward reference is unnecessary.

♻️ Proposed change
-    def from_timeout(cls, job_name: str, timeout: float | None) -> "_WatchDeadline":
+    def from_timeout(cls, job_name: str, timeout: float | None) -> _WatchDeadline:

As per coding guidelines: "Prefer concrete type hints over string-based type hints".

🤖 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_plugin/src/nemo_platform_plugin/jobs/watch.py` around
lines 101 - 104, Update the return annotation on the _WatchDeadline.from_timeout
classmethod to use the concrete _WatchDeadline type directly instead of a quoted
string, relying on the active future annotations import.

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_plugin/src/nemo_platform_plugin/jobs/watch.py`:
- Around line 340-353: Update _status_event to propagate
PlatformJobStatusResponse.error_details into the returned JobStatusEvent,
preserving the existing status and status_details mappings so failed-job
renderers can display the failure reason.
- Around line 480-496: The _new_log_events function lets state.seen_logs grow
indefinitely; bound its retained entries so deduplication covers only the
current and previous drain, or entries newer than the persisted cursor. Update
the surrounding _WatchState bookkeeping as needed while preserving
occurrence-based suppression of already-seen log events.
- Around line 33-35: Update the terminal-status definitions near
_SUCCESSFUL_TERMINAL_STATUSES, _FAILED_TERMINAL_STATUSES, and _TERMINAL_STATUSES
to account for the paused PlatformJobStatus. Ensure nemo jobs watch and create
--watch stop polling when a job is paused, or emit the required warning event if
paused is intentionally non-terminal.

---

Outside diff comments:
In
`@tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py`:
- Around line 2120-2125: Update the control flow around _ClientMethodReplacer so
an empty collector.class_methods collection does not return before stale
cleanup. Always visit the target tree with _ClientMethodReplacer and then
_ClientStaleImportRemover, allowing stale watch_job methods and imports to be
removed even when no replacement methods exist.

In `@tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py`:
- Around line 814-826: Update the fixture setup for client_path before invoking
_replace_client_methods to add stale AsyncIterator and Iterator imports plus a
watch_job method and jobs.watch reference. Keep the existing assertions
unchanged so the test verifies these stale symbols are actually removed rather
than passing when no cleanup is needed.

---

Nitpick comments:
In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py`:
- Around line 101-104: Update the return annotation on the
_WatchDeadline.from_timeout classmethod to use the concrete _WatchDeadline type
directly instead of a quoted string, relying on the active future annotations
import.
🪄 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: 9870e250-8e31-4048-be78-6615fe0b836e

📥 Commits

Reviewing files that changed from the base of the PR and between f206b4f and 9fe6249.

⛔ Files ignored due to path filters (14)
  • 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/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/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
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch_types.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 (14)
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/adapter.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/inference/deployments/init.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • docs/cli/reference.mdx
  • packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/api/jobs/init.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/config.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py

@ironcommit
ironcommit requested a review from tylersbray August 6, 2026 03:54
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from 9fe6249 to 1847407 Compare August 6, 2026 03:55
@coderabbitai

coderabbitai Bot commented Aug 6, 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: 3

Caution

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

⚠️ Outside diff range comments (1)
packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py (1)

367-374: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Set verbose=False for the --wait assertion. create_deployments passes verbose=watch, so --wait is quiet and --watch remains verbose=True.

🤖 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/commands/test_create_wait.py` around
lines 367 - 374, Update the wait-mode assertion for create_deployments to expect
verbose=False, reflecting that --wait passes verbose=watch and remains quiet;
preserve verbose=True for the --watch assertion.
🧹 Nitpick comments (3)
packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py (2)

167-170: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Drop the string-based return annotation.

The module has from __future__ import annotations, so use _WatchDeadline directly.

♻️ Proposed change
-    def from_timeout(cls, job_name: str, timeout: float | None) -> "_WatchDeadline":
+    def from_timeout(cls, job_name: str, timeout: float | None) -> _WatchDeadline:

As per coding guidelines: "Prefer concrete type hints over string-based type hints".

🤖 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_plugin/src/nemo_platform_plugin/jobs/watch.py` around
lines 167 - 170, Update the _WatchDeadline.from_timeout classmethod return
annotation to reference _WatchDeadline directly instead of using a quoted
string, while preserving the existing timeout and return behavior.

Source: Coding guidelines


444-459: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Access error_details directly.

PlatformJobStatusResponse declares error_details as a field. The getattr fallback hides schema drift from ty and reads as if the field is optional on the model.

♻️ Proposed change
-    error_details = getattr(status_response, "error_details", None)
+    error_details = status_response.error_details
🤖 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_plugin/src/nemo_platform_plugin/jobs/watch.py` around
lines 444 - 459, Update _status_event to access status_response.error_details
directly instead of using getattr, preserving the existing None handling and
conversion to a dictionary for the JobStatusEvent error_details field.
packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py (1)

66-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

The renderer imports a private symbol from waiters.

_emit_job_run_event is private to waiters. Promote it to a public helper, or move it to a shared telemetry module, so the coupling survives refactors of waiters.

🤖 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/job_watch_renderer.py`
around lines 66 - 76, Update _emit_terminal_job_run_event to stop importing the
private waiters._emit_job_run_event symbol directly. Promote _emit_job_run_event
to a public helper or relocate it to a shared telemetry module, then update both
its definition and callers to use the public/shared API while preserving the
existing event arguments and 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/cli/core/job_watch_renderer.py`:
- Around line 107-109: The _time_label function must render aware timestamps in
local time to match status and warning lines; convert the provided timestamp
with astimezone() before strftime while preserving datetime.now() behavior for
omitted timestamps. In
packages/nemo_platform_ext/tests/cli/core/test_job_watch_renderer.py lines
34-56, add an assertion that the log line’s time prefix matches the expected
local-time conversion.

In `@packages/nemo_platform_ext/tests/jobs/test_watch.py`:
- Around line 1-27: Update the pytest configuration used by the repository to
set pytest’s import mode to importlib, preventing import-file mismatches when
both test_watch.py modules are collected. Add the setting in the root pytest
configuration rather than changing the test module imports.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py`:
- Around line 680-685: Update _is_invalid_page_cursor_error to rely on a stable
documented machine-readable invalid-cursor code instead of exact
NemoHTTPError.detail text; if no such code is available, add a narrowly scoped
compatibility check covering the known PageCursor.decode signal and tests for
the fallback through _can_retry_log_scan_from_start.

---

Outside diff comments:
In `@packages/nemo_platform_ext/tests/cli/commands/test_create_wait.py`:
- Around line 367-374: Update the wait-mode assertion for create_deployments to
expect verbose=False, reflecting that --wait passes verbose=watch and remains
quiet; preserve verbose=True for the --watch assertion.

---

Nitpick comments:
In
`@packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/job_watch_renderer.py`:
- Around line 66-76: Update _emit_terminal_job_run_event to stop importing the
private waiters._emit_job_run_event symbol directly. Promote _emit_job_run_event
to a public helper or relocate it to a shared telemetry module, then update both
its definition and callers to use the public/shared API while preserving the
existing event arguments and behavior.

In `@packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py`:
- Around line 167-170: Update the _WatchDeadline.from_timeout classmethod return
annotation to reference _WatchDeadline directly instead of using a quoted
string, while preserving the existing timeout and return behavior.
- Around line 444-459: Update _status_event to access
status_response.error_details directly instead of using getattr, preserving the
existing None handling and conversion to a dictionary for the JobStatusEvent
error_details field.
🪄 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: ae2a3306-9806-473a-9609-852f4795139f

📥 Commits

Reviewing files that changed from the base of the PR and between 747fe9f and 1847407.

⛔ Files ignored due to path filters (14)
  • 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/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 (27)
  • 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/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
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch_types.py
  • packages/nemo_platform_plugin/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 (18)
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/overrides/jobs/watch.py
  • packages/nemo_platform_ext/tests/cli/test_app.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/context_collectors/create_collector.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/client/adapter.py
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/cli_config.yaml
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch_types.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
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/cli_generator/config.py
  • tools/nemo-platform-sdk-tools/tests/sdk/vendor/test_vendor_package.py
  • tools/nemo-platform-sdk-tools/tests/sdk/cli_generator/test_config.py
  • packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/client.py
  • packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py
  • docs/cli/reference.mdx
  • tools/nemo-platform-sdk-tools/src/nemo_platform_sdk_tools/sdk/vendor/vendor_package.py
  • packages/nemo_platform_ext/tests/cli/telemetry/test_job_events.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/core/waiters.py

Comment thread packages/nemo_platform_ext/tests/jobs/test_watch.py
Comment thread packages/nemo_platform_plugin/src/nemo_platform_plugin/jobs/watch.py Outdated
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from 1847407 to 58580df Compare August 6, 2026 04:21

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

some questions but lg

Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/cli/core/code_generator.py Outdated
Comment thread pytest.ini
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 2 times, most recently from 2c533b9 to 984a97c Compare August 7, 2026 02:36
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
Signed-off-by: Ryan S <267728323+ironcommit@users.noreply.github.com>
@ironcommit
ironcommit force-pushed the jobs-watch-sdk-first/rsadler branch from 984a97c to 674025a Compare August 7, 2026 14:59
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.

4 participants