Skip to content

feat: provision local ClickHouse for Intake - #1113

Merged
BrianNewsom merged 20 commits into
mainfrom
clickhouse-local-provisioning/brnewsom
Aug 7, 2026
Merged

feat: provision local ClickHouse for Intake#1113
BrianNewsom merged 20 commits into
mainfrom
clickhouse-local-provisioning/brnewsom

Conversation

@BrianNewsom

@BrianNewsom BrianNewsom commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Intake now provisions an isolated, data-directory-owned ClickHouse container when no external URL is configured, while preserving explicit external ClickHouse ownership. Managed containers use dynamic loopback ports, stop without data deletion on graceful platform shutdown, and restart on the next startup.

Changes

  • Reconcile local ClickHouse by durable data-directory identity with safe reuse, stale-container replacement, and fail-closed ownership validation.
  • Keep Intake available with 503 responses when Docker or ClickHouse provisioning fails, with actionable recovery guidance.
  • Stop managed ClickHouse on graceful Intake shutdown, preserve its container and bind-mounted data, and disable Docker automatic restart.
  • Preserve the legacy fixed-port script interface while using the same Python provisioner and explicit external URL contract.
  • Guard teardown so managed containers are removed before their platform-owned data can be deleted.
  • Update Intake, setup, SDK-vendored skill, Insights, evaluator, and test-harness integrations for the managed lifecycle.
  • Suppress only the expected clickhouse-connect connection warning during readiness polling while retaining final timeout failures and unrelated warnings.
  • Pin the Insights test stack's ClickHouse URL into both its compatibility launcher and platform subprocess, with configured-port and rejection coverage.
  • Repair the existing OTLP smoke script so its built-in readback uses Intake's public source filter.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification:

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

  • uv run --frozen pytest services/intake/tests/test_local_clickhouse.py services/intake/tests/test_clickhouse_startup.py plugins/nemo-insights/tests/testbed/test_eval_scripts.py -q — 55 passed.
  • uv run --frozen pytest services/intake/tests/integration/test_local_clickhouse_provisioning.py -v --log-cli-level=WARNING — 1 real-Docker create/reuse/dynamic-port/restart-policy/removal test passed using temporary data.
  • Focused uv run --frozen ruff check ... and uv run --frozen ruff format --check ... — passed.
  • uv run --frozen ty check services/intake/src/nmp/intake/local_clickhouse.py services/intake/src/nmp/intake/service.py plugins/nemo-insights/testbed/eval/stack.py — passed.
  • make vendor-nemo-platform-ext — passed; the generated teardown skill mirror is byte-identical to its package source.
  • Manual local E2E — nemo services start provisioned ClickHouse, send_otel_sample.py wrote and read back sample-chain and sample-llm, the spans API returned both rows, and nemo services stop stopped the managed container.
  • uv run pre-commit run -a — all backend/config hooks passed. studio-lint-staged was blocked by the local unrelated Studio environment: Node v24.18.0 does not satisfy >=22.23.2 <23, and lint-staged is not installed. No web/ files changed.
  • Full uv run --frozen pytest services/intake/tests -q was stopped after 125 passing tests in 5m34s when it entered the broad real-ClickHouse integration matrix; no failure was observed. The directly affected unit and isolated Docker lifecycle tests above completed successfully.
  • DCO audit — all 18 commits in origin/main..HEAD contain a matching Signed-off-by: trailer.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom
BrianNewsom requested review from a team as code owners August 5, 2026 21:35
@github-actions github-actions Bot added the feat label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread services/intake/src/nmp/intake/local_clickhouse.py Fixed
Comment thread services/intake/src/nmp/intake/local_clickhouse.py Fixed
@coderabbitai

coderabbitai Bot commented Aug 5, 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
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Intake now provisions and reuses local ClickHouse containers when no external URL is configured. Startup applies the provisioned URL and continues with recovery guidance when provisioning fails. Reset and teardown remove managed containers before deleting bind-mounted data.

Changes

Local ClickHouse lifecycle

Layer / File(s) Summary
Provisioner configuration and lifecycle
services/intake/src/nmp/intake/config.py, services/intake/src/nmp/intake/local_clickhouse.py, services/intake/scripts/spans/run_clickhouse.sh, services/intake/pyproject.toml, packages/nemo_platform/pyproject.toml
Intake manages ClickHouse containers with persistent data directories, dynamic loopback ports, ownership checks, readiness checks, compatibility handling, and safe removal.
Service startup and validation
services/intake/src/nmp/intake/service.py, services/intake/tests/*
Startup applies provisioned URLs and logs recovery guidance while remaining ready when provisioning fails. Tests cover reuse, replacement, removal safety, Docker failures, external URLs, integration behavior, and import boundaries.
Safe reset and teardown
SETUP.md, packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md, packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-teardown/SKILL.md
Reset and teardown remove owned ClickHouse containers before deleting bind-mounted platform data. Externally configured ClickHouse data remains preserved.
Setup and operational guidance
README.md, SETUP.md, docs/*, packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-intake/SKILL.md, plugins/*/examples/*/README.md, services/intake/README.md, plugins/nemo-insights/scripts/insights_demo.py, plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
Documentation replaces manual ClickHouse startup with automatic provisioning. It documents Docker prerequisites, reuse, external URL configuration, teardown, and recovery behavior.
Dynamic container resolution
plugins/nemo-insights/testbed/stack.py, plugins/nemo-insights/testbed/reingest.py, plugins/nemo-insights/tests/testbed/*
Insights resolves managed ClickHouse containers by Docker labels and preserves records when resolution or cleanup fails.
E2E service data layout
e2e/services_pool.py, packages/nmp_testing/tests/unit/test_e2e_harness.py
E2E service data now uses a sibling directory outside the log directory. The harness test verifies the new path.

Sequence Diagram(s)

sequenceDiagram
  participant IntakeService
  participant LocalClickHouse
  participant Docker
  participant ClickHouse
  IntakeService->>LocalClickHouse: request local provisioning
  LocalClickHouse->>Docker: validate or create managed container
  Docker->>ClickHouse: start container and expose loopback port
  LocalClickHouse->>ClickHouse: wait for readiness
  LocalClickHouse-->>IntakeService: return effective ClickHouse URL
Loading

Possibly related PRs

Suggested reviewers: mckornfield, svvarom, benmccown

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% 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
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: local ClickHouse provisioning for Intake.
✨ 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 clickhouse-local-provisioning/brnewsom

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

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>

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

🧹 Nitpick comments (5)
services/intake/tests/test_local_clickhouse.py (1)

139-143: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the already-reachable short circuit.

_patch_provisioning forces _can_connect to False, and every provisioning test uses it. The first branch of _provision_local_clickhouse, which returns settings.url without touching Docker, is therefore never exercised. That branch is the reuse path this PR advertises, and it must not create a container.

Add one test with _can_connect returning True and assert run_calls == [] and that docker.from_env is not called.

🤖 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 `@services/intake/tests/test_local_clickhouse.py` around lines 139 - 143, Add a
focused test for _provision_local_clickhouse where _can_connect returns True,
asserting it returns the existing settings.url without invoking Docker
provisioning. Ensure the test verifies the fake client’s run_calls remains empty
and docker.from_env is not called, while keeping _patch_provisioning unchanged
for the existing tests.
services/intake/src/nmp/intake/config.py (1)

16-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Read the ClickHouse version from .clickhouse-version.

local_clickhouse.CLICKHOUSE_VERSION aliases this hardcoded constant, while run_clickhouse.sh uses services/intake/.clickhouse-version. A version bump can leave the Python defaults at 26.3. Load the default from the version file or test DEFAULT_CLICKHOUSE_VERSION against it.

🤖 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 `@services/intake/src/nmp/intake/config.py` around lines 16 - 19, Update
DEFAULT_CLICKHOUSE_VERSION in the configuration constants and the derived
DEFAULT_CLICKHOUSE_IMAGE to use the version declared in
services/intake/.clickhouse-version instead of maintaining the hardcoded 26.3
value. Ensure the existing local_clickhouse.CLICKHOUSE_VERSION alias continues
to reflect the file-based version used by run_clickhouse.sh.
services/intake/src/nmp/intake/local_clickhouse.py (2)

96-98: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Implicit adoption of an unmanaged ClickHouse at the default URL.

should_provision_local_clickhouse is true only when no external URL is set, so settings.url here is the default http://localhost:8123. If an unrelated ClickHouse listens there, Intake adopts it, skips container provisioning, and writes Intake data into a database it does not own. The integration test comment at services/intake/tests/integration/test_local_clickhouse_provisioning.py Line 55 acknowledges this case on developer machines.

Consider restricting this fast path to an explicitly configured URL, and always managing the container otherwise.

🤖 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 `@services/intake/src/nmp/intake/local_clickhouse.py` around lines 96 - 98,
Update the _can_connect fast path in should_provision_local_clickhouse so an
already-reachable ClickHouse is reused only when the URL was explicitly
configured as an external endpoint; when using the default localhost URL,
continue with local container provisioning instead of adopting the unmanaged
service.

110-112: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Validate the legacy container mount before adoption. The fallback passes data_dir=None to _validate_container, so a same-named container with the expected image but no /var/lib/clickhouse mount is accepted for Intake writes. Require the CLICKHOUSE_DATA_PATH mount while preserving compatibility with legacy labels.

🤖 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 `@services/intake/src/nmp/intake/local_clickhouse.py` around lines 110 - 112,
Update the legacy fallback around _get_container so the selected container is
validated with CLICKHOUSE_DATA_PATH mounted before setting legacy and adopting
it. Preserve compatibility with legacy labels while rejecting same-named
containers that lack the required ClickHouse data mount.
services/intake/tests/test_clickhouse_startup.py (1)

60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the local IntakeConfig in the assertion.

Service.service_config is typed as TConfig | None. Direct access to service.service_config.clickhouse_config can fail ty checking.

🤖 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 `@services/intake/tests/test_clickhouse_startup.py` around lines 60 - 63,
Update the assertion in the startup test to reuse the local IntakeConfig
instance when obtaining the expected ClickHouse image and data directory,
instead of accessing the optional service.service_config value. Keep the
existing provision.await_args.kwargs expectations unchanged.

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/skills/inference/SKILL.md`:
- Around line 526-534: Add blank lines immediately before and after the fenced
bash block in the reset local platform state section, without changing the
commands or surrounding content.
- Around line 528-537: Update the teardown script around the ClickHouse cleanup
command to mirror the guard used in nemo-teardown: stop immediately when `python
-m nmp.intake.local_clickhouse --remove` fails, before executing `rm -rf
"$DATA_DIR"`. Also add the same refusal for an unsafe or explicitly configured
`DATA_DIR`, while preserving cleanup of an external `CLICKHOUSE_DATA_DIR` and
deletion only after successful removal.

In `@services/intake/scripts/spans/run_clickhouse.sh`:
- Around line 19-20: Update the compatibility path around the local ClickHouse
launcher in nmp.intake.local_clickhouse so it preserves the previous script’s
permission handling: apply chmod 755 to the configured
NMP_INTAKE_CLICKHOUSE_DATA_DIR and its tmp directory before starting ClickHouse,
while retaining manage_permissions=False for the data_dir flow.

In `@services/intake/src/nmp/intake/local_clickhouse.py`:
- Around line 166-179: Update remove_local_clickhouse to check both
_managed_container_name(resolved_data_dir) and LEGACY_CONTAINER_NAME, removing
any matching container after applying _validate_cleanup_target with the same
data-directory mount validation. Preserve the existing return and logging
behavior while ensuring legacy containers created by main or run_clickhouse.sh
are removed before cleanup proceeds.

In `@SETUP.md`:
- Around line 62-74: Update the cleanup snippet in SETUP.md to gate rm -rf
"$DATA_DIR" on successful completion of the local_clickhouse --remove command.
Use the document’s established uv run command form, and ensure a Docker cleanup
failure exits or otherwise prevents deletion so the user must start Docker and
retry.

---

Nitpick comments:
In `@services/intake/src/nmp/intake/config.py`:
- Around line 16-19: Update DEFAULT_CLICKHOUSE_VERSION in the configuration
constants and the derived DEFAULT_CLICKHOUSE_IMAGE to use the version declared
in services/intake/.clickhouse-version instead of maintaining the hardcoded 26.3
value. Ensure the existing local_clickhouse.CLICKHOUSE_VERSION alias continues
to reflect the file-based version used by run_clickhouse.sh.

In `@services/intake/src/nmp/intake/local_clickhouse.py`:
- Around line 96-98: Update the _can_connect fast path in
should_provision_local_clickhouse so an already-reachable ClickHouse is reused
only when the URL was explicitly configured as an external endpoint; when using
the default localhost URL, continue with local container provisioning instead of
adopting the unmanaged service.
- Around line 110-112: Update the legacy fallback around _get_container so the
selected container is validated with CLICKHOUSE_DATA_PATH mounted before setting
legacy and adopting it. Preserve compatibility with legacy labels while
rejecting same-named containers that lack the required ClickHouse data mount.

In `@services/intake/tests/test_clickhouse_startup.py`:
- Around line 60-63: Update the assertion in the startup test to reuse the local
IntakeConfig instance when obtaining the expected ClickHouse image and data
directory, instead of accessing the optional service.service_config value. Keep
the existing provision.await_args.kwargs expectations unchanged.

In `@services/intake/tests/test_local_clickhouse.py`:
- Around line 139-143: Add a focused test for _provision_local_clickhouse where
_can_connect returns True, asserting it returns the existing settings.url
without invoking Docker provisioning. Ensure the test verifies the fake client’s
run_calls remains empty and docker.from_env is not called, while keeping
_patch_provisioning unchanged for the existing tests.
🪄 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: 4bbf51ba-56a5-4b6d-a4cf-9513eabcb2d3

📥 Commits

Reviewing files that changed from the base of the PR and between 73670d7 and b0c715f.

⛔ Files ignored due to path filters (4)
  • sdk/python/nemo-platform/src/nemo_platform/skills/inference/SKILL.md is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/skills/nemo-intake/SKILL.md is excluded by !sdk/**
  • sdk/python/nemo-platform/src/nemo_platform/skills/nemo-teardown/SKILL.md is excluded by !sdk/**
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • README.md
  • SETUP.md
  • docs/agents/observability.mdx
  • docs/evaluator/experiments.mdx
  • docs/get-started/example-agent.mdx
  • packages/nemo_platform/pyproject.toml
  • packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md
  • packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-intake/SKILL.md
  • packages/nemo_platform_ext/src/nemo_platform_ext/skills/nemo-teardown/SKILL.md
  • plugins/nemo-agents/examples/nemo-agent-config/README.md
  • plugins/nemo-insights/examples/research-agent/README.md
  • services/intake/README.md
  • services/intake/pyproject.toml
  • services/intake/scripts/spans/run_clickhouse.sh
  • services/intake/src/nmp/intake/config.py
  • services/intake/src/nmp/intake/local_clickhouse.py
  • services/intake/src/nmp/intake/service.py
  • services/intake/tests/integration/test_local_clickhouse_provisioning.py
  • services/intake/tests/test_clickhouse_architecture.py
  • services/intake/tests/test_clickhouse_startup.py
  • services/intake/tests/test_local_clickhouse.py

Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md Outdated
Comment thread packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md Outdated
Comment thread services/intake/scripts/spans/run_clickhouse.sh Outdated
Comment thread services/intake/src/nmp/intake/local_clickhouse.py Outdated
Comment thread SETUP.md Outdated
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Addressed the review in e8f3e39. The default local path no longer adopts an arbitrary ClickHouse already listening on localhost:8123; explicit NMP_INTAKE_CLICKHOUSE_URL remains the external-service bypass, so the suggested reachable-short-circuit test is no longer applicable. Legacy reuse now requires the exact expected data mount. The startup test now uses its concrete IntakeConfig and narrows await_args for ty.

DEFAULT_CLICKHOUSE_VERSION remains a package-safe constant because the service version file is not available from every installed-package layout. Existing test_clickhouse_image_version_matches_service_pin already asserts exact equality with services/intake/.clickhouse-version, preventing drift in CI.

Validation: 240 Intake unit tests, focused Ruff and ty, repository pre-commit hooks, and the real Docker create/reuse/remove integration all pass.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --stat --oneline --decorate --no-renames e8f3e3904a
git show --format=fuller --no-ext-diff --unified=80 --no-renames e8f3e3904a -- \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/src/nmp/intake/config.py \
  services/intake/src/nmp/intake/service.py \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/test_clickhouse_startup.py \
  services/intake/tests/test_clickhouse_image_version.py \
  services/intake/tests

Length of output: 50478


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class

sed -n '1,145p' services/intake/tests/test_local_clickhouse.py
sed -n '1,130p' services/intake/scripts/spans/run_clickhouse.sh
sed -n '1,240p' services/intake/src/nmp/intake/local_clickhouse.py

rg -n -C 5 '\bremove_local_clickhouse\s*\(|--remove\b|nmp\.intake\.local_clickhouse' \
  services/intake SETUP.md README.md packages/nemo_platform_ext \
  -g '*.py' -g '*.sh' -g '*.md' -g '*.mdx'

Length of output: 41843


@BrianNewsom One issue remains.

_provision_local_clickhouse() can reuse a legacy nmp-intake-clickhouse container when its image, credentials, and data mount match. This path accepts label-less legacy containers. remove_local_clickhouse() then finds the same container, but _validate_cleanup_target() rejects it because it requires the new managed labels.

As a result, a pre-existing legacy container can start successfully, but documented --remove teardown fails and prevents deletion of its data directory.

Make legacy cleanup accept the same verified legacy ownership criteria as legacy reuse, or migrate the legacy container to the managed labels. Add a test for a label-less, same-mount legacy container that is reused and then removed.


✏️ Learnings added
Learnt from: BrianNewsom
URL: https://github.com/NVIDIA-NeMo/nemo-platform/pull/1113

Timestamp: 2026-08-05T21:53:40.743Z
Learning: In `services/intake`, `DEFAULT_CLICKHOUSE_VERSION` must remain a package-safe constant because `services/intake/.clickhouse-version` is not available in every installed-package layout. `services/intake/tests/test_local_clickhouse.py::test_clickhouse_image_version_matches_service_pin` enforces equality with the repository version file in CI.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

Fixed the remaining CodeRabbit finding in ca71c2b. Cleanup now accepts a label-less historical container only under the legacy name with the exact resolved ClickHouse data mount; partial or conflicting ownership labels still fail closed. The regression test now reuses that label-less legacy container and removes it through the documented cleanup path. All 240 Intake unit tests, focused Ruff and ty, and repository pre-commit hooks pass.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

Addressed the Python e2e tests (prebuilt image) CI failure in c99eced. The tests themselves completed; artifact upload failed while traversing a container-owned ClickHouse access directory under E2E_SERVICES_LOG_DIR. E2E persistent state now lives in a sibling data directory, keeping generated configs and service logs collectable without archiving platform state. Validation: all 58 nmp_testing package tests, focused Ruff and ty, and repository pre-commit hooks pass.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 31438/40061 78.5% 63.0%
Integration Tests 18307/38013 48.2% 20.8%

@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Addressed the Linux Docker integration failure in d0e7e26. Docker SDK encoded the tuple with a None host port as an empty HostPort; the Linux daemon produced an occupied binding. The managed path now passes host port 0 explicitly, preserving loopback-only ephemeral allocation. Focused unit, Ruff, ty, repository pre-commit, and the real Docker create/reuse/remove integration pass.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --format=fuller --stat --no-ext-diff --no-renames d0e7e26b27
git show --format= --unified=100 --no-ext-diff --no-renames d0e7e26b27 -- \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/integration/test_local_clickhouse_provisioning.py

ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
rg -n -C 6 'ports|HostPort|127\.0\.0\.1|provision_local_clickhouse' \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/integration/test_local_clickhouse_provisioning.py

Length of output: 55799


@BrianNewsom No actionable issue found.

The managed path now requests ("127.0.0.1", 0). The unit test asserts this binding. The integration test verifies a loopback-assigned port and container reuse.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

The latest commit serializes the new local ClickHouse provisioning integration test with the existing evaluator integration tests via their shared xdist resource group. Local xdist verification placed all three tests on one worker in sequence.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR head =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'

echo '== Latest commit =='
git log -1 --format='%H%n%s%n%b'

echo '== Latest commit changed files and diff =='
git show --stat --oneline --no-renames HEAD
git show --format= --no-ext-diff --unified=80 --no-renames HEAD

echo '== Integration test structure =='
ast-grep outline services/intake/tests/integration/test_local_clickhouse_provisioning.py --items all --type function,class

echo '== Resource-group configuration and usages =='
rg -n -C 5 --glob '*.py' --glob '*.toml' --glob '*.ini' --glob '*.cfg' \
  'xdist_group|resource[_ -]?group|pytest\.mark\.xdist' \
  services e2e packages pyproject.toml pytest.ini setup.cfg tox.ini 2>/dev/null || true

Length of output: 7145


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Candidate evaluator and ClickHouse integration tests =='
fd -t f . | rg '(evaluator|clickhouse).*test.*\.py$|test.*(evaluator|clickhouse).*\.py$' || true

echo '== All xdist groups and ClickHouse test references =='
rg -n -i -C 4 \
  'xdist_group|nmp_intake_clickhouse|clickhouse' \
  services e2e packages .github pyproject.toml pytest.ini setup.cfg tox.ini \
  -g '*.py' -g '*.toml' -g '*.yml' -g '*.yaml' -g '*.ini' -g '*.cfg' 2>/dev/null || true

echo '== pytest-xdist distribution configuration and CI invocations =='
rg -n -C 4 -- \
  '--dist(=|\s+)(loadgroup|load|worksteal|each)|-n\s*(auto|[0-9]+)|pytest-xdist|xdist' \
  .github services e2e packages pyproject.toml pytest.ini setup.cfg tox.ini \
  -g '*.py' -g '*.toml' -g '*.yml' -g '*.yaml' -g '*.ini' -g '*.cfg' -g '*.sh' 2>/dev/null || true

Length of output: 201366


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Shared resource-group markers =='
git grep -n -C 3 'nmp_intake_clickhouse' -- '*.py' '*.toml' '*.ini' '*.cfg' '*.yml' '*.yaml' '*.md' '*.sh' || true

echo '== Every xdist_group marker in Intake tests =='
git grep -n -C 3 'xdist_group' -- 'services/intake/**' || true

echo '== Intake test execution definitions =='
git grep -n -i -C 4 -E \
  'services/intake/tests|test_local_clickhouse_provisioning|test_evaluator|pytest.*integration|integration.*pytest' \
  -- '.github/**' 'services/intake/**' 'Makefile' 'Taskfile*' 'justfile' '*.toml' '*.yaml' '*.yml' '*.md' '*.sh' 2>/dev/null || true

echo '== Group-aware xdist scheduler configuration =='
git grep -n -C 4 -E -- '--dist[=[:space:]]*loadgroup|dist[[:space:]]*=[[:space:]]*"loadgroup"|dist[[:space:]]*=[[:space:]]*loadgroup' \
  -- ':!uv.lock' ':!**/*.lock' || true

Length of output: 19943


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Evaluator integration conftest map =='
ast-grep outline plugins/nemo-evaluator/tests/integration/conftest.py --items all --type function,class

echo '== Shared ClickHouse group implementation =='
rg -n -C 12 \
  'CLICKHOUSE_XDIST_GROUP|CLICKHOUSE_XDIST_FIXTURE|pytest_collection_modifyitems|xdist_group|def _clickhouse' \
  plugins/nemo-evaluator/tests/integration/conftest.py

echo '== Makefile pytest distribution expansion =='
sed -n '285,350p' Makefile

Length of output: 7529


@BrianNewsom No actionable issue found.

The provisioning test uses nmp_intake_clickhouse. Evaluator tests that use _clickhouse receive the same group during collection. make test-integration and make test-integration-ci use --dist loadgroup, so xdist schedules these tests on one worker.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

The latest commit fixes Linux reuse after the ClickHouse image changes ownership of the bind mount. The marker remains mode 0600; provisioning now derives identity from stable filesystem metadata instead of reading owner-only contents. Focused unit, type, lint, and real-Docker create/reuse/remove tests pass.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR head and latest commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git log -1 --format='%H%n%s%n%b'

echo '== Latest commit diff =='
git show --stat --oneline --no-renames HEAD
git show --format= --no-ext-diff --unified=100 --no-renames HEAD -- \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/integration/test_local_clickhouse_provisioning.py

echo '== Implementation and test map =='
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class

echo '== Identity marker, filesystem metadata, and reuse references =='
rg -n -C 12 \
  'marker|identity|stat\(|st_(dev|ino|uid|gid|mode)|chmod|0600|data_dir|reuse|mount' \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/integration/test_local_clickhouse_provisioning.py

Length of output: 112366


@BrianNewsom One actionable issue remains.

_prepare_data_dir() uses st_dev, st_ino, st_mtime_ns, and st_size as the data-instance ID. This tuple does not guarantee a new ID after marker recreation. A filesystem can reuse an inode, and timestamp precision can be coarser than nanoseconds. If the recreated empty marker has the same tuple, the old container label matches and bypasses stale-container replacement.

Use an identity source that remains readable after ClickHouse changes ownership and is unique per marker creation. Add a test that forces identical marker metadata across two marker incarnations and verifies that the stale container is replaced.

You are interacting with an AI system.

@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Added the guarded nemo services reset-data workflow in a4c7e85: centralized teardown/data-dir safety checks, managed ClickHouse cleanup with host ownership restoration, Docker-down guidance that preserves data, canonical + vendored CLI tests, and generated docs. Also removed the raw inference-skill data deletion noted in the previous review.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Commit and changed files =='
git show --stat --oneline --no-renames a4c7e85457
git show --format= --no-ext-diff --unified=100 --no-renames a4c7e85457

echo '== Candidate reset and teardown files =='
fd -t f . | rg '(reset|teardown|service|clickhouse|SKILL|SETUP).*($|\.py$|\.md$|\.mdx$)' | head -300

echo '== Reset-data definitions, callers, and tests =='
rg -n -C 8 --glob '*.py' --glob '*.md' --glob '*.mdx' --glob '*.sh' \
  'reset-data|reset_data|remove_local_clickhouse|ownership restoration|chown|Docker.*unavailable' \
  . 2>/dev/null || true

Length of output: 213566


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== CLI reset helpers with line numbers =='
nl -ba packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py | sed -n '55,130p;410,510p'

echo '== Port bindability and instance enumeration =='
rg -n -C 10 --glob '*.py' \
  'def is_port_bindable|def list_instances|def nmp_user_data_dir|class ClickHouseConfig|def _restore_data_dir_ownership|def remove_local_clickhouse' \
  packages/nemo_platform_ext packages/nemo_platform_plugin services/intake

echo '== Reset-data test coverage and CLI copy parity =='
rg -n -C 4 --glob '*.py' \
  'reset-data|reset_services_data|is_port_bindable|restore_data_ownership|Could not restore' \
  packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests services/intake/tests

echo '== Copy parity =='
cmp -s \
  packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
  sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
  && echo 'CLI copies match' || echo 'CLI copies differ'

Length of output: 1409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== CLI reset helpers =='
sed -n '55,130p;410,510p' packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py

echo '== Port bindability and instance enumeration =='
rg -n -C 10 --glob '*.py' \
  'def is_port_bindable|def list_instances|def nmp_user_data_dir|class ClickHouseConfig|def _restore_data_dir_ownership|def remove_local_clickhouse' \
  packages/nemo_platform_ext packages/nemo_platform_plugin services/intake || true

echo '== Reset-data test coverage =='
rg -n -C 4 --glob '*.py' \
  'reset-data|reset_services_data|is_port_bindable|restore_data_ownership|Could not restore' \
  packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests services/intake/tests || true

echo '== CLI copy parity =='
cmp -s \
  packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
  sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
  && echo 'CLI copies match' || echo 'CLI copies differ'

Length of output: 38333


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Data-directory propagation =='
rg -n -C 8 --glob '*.py' \
  'NMP_DATA_DIR|local_services\.data_dir|data_dir.*local_services|nmp_user_data_dir\(' \
  packages/nemo_platform_ext sdk/python/nemo-platform packages/nemo_platform_plugin packages/nmp_common \
  services 2>/dev/null || true

echo '== Config and runner symbols =='
ast-grep outline packages/nemo_platform_ext/src --items all --type function,class --match 'Config|PlatformAppConfig|run_platform'
ast-grep outline packages/nmp_common/src --items all --type function,class --match 'Config|PlatformAppConfig|run_platform'

echo '== Canonical and vendored CLI checksums =='
sha256sum \
  packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
  sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py

Length of output: 71640


@BrianNewsom No actionable issue found.

reset-data resolves the same persisted data directory that service startup forwards through NMP_DATA_DIR. It blocks live tracked instances and port 8080, removes managed ClickHouse before deletion, restores ownership for in-tree ClickHouse data, and preserves platform data if ClickHouse cleanup fails. The raw inference-skill deletion is removed.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Removed the newly introduced nemo services reset-data command in 4abb23e. nemo services rm/prune remain limited to stopped-instance metadata/log cleanup. Platform-data deletion stays only in the existing setup/teardown procedure; the existing Intake --remove helper now restores ownership for in-tree ClickHouse data while preserving external data.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --format='%H%n%s%n%b' --no-patch 4abb23e893

echo '== Changed files and patch =='
git show --stat --oneline --no-renames 4abb23e893
git show --format= --no-ext-diff --unified=100 --no-renames 4abb23e893

echo '== Relevant source structure =='
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py --items all --type function,class
ast-grep outline sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py --items all --type function,class

echo '== Cleanup, ownership, and removed-command references =='
rg -n -C 8 --glob '*.py' --glob '*.md' --glob '*.mdx' --glob '*.sh' \
  'reset-data|reset_data|remove_local_clickhouse|restore.*ownership|chown|NMP_DATA_DIR|data_dir' \
  services/intake packages/nemo_platform_ext sdk/python/nemo-platform SETUP.md README.md

echo '== Relevant test structure and coverage =='
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class
rg -n -C 6 --glob '*.py' \
  'remove_local_clickhouse|restore.*ownership|ownership|external.*data|data_dir' \
  services/intake/tests packages/nemo_platform_ext/tests sdk/python/nemo-platform/tests 2>/dev/null || true

echo '== Canonical/vendored CLI parity =='
cmp -s \
  packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
  sdk/python/nemo-platform/src/nemo_platform/cli/commands/services/cli.py \
  && echo 'CLI copies match' || echo 'CLI copies differ'

Length of output: 202366


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Commit files =='
git diff-tree --no-commit-id --name-status -r 4abb23e893

echo '== Target patch only =='
git show --format= --no-ext-diff --unified=50 --no-renames 4abb23e893 -- \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/tests/test_local_clickhouse.py \
  packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py \
  packages/nemo_platform_ext/tests \
  README.md SETUP.md

echo '== Current relevant implementation =='
sed -n '160,207p;344,395p;532,567p' services/intake/src/nmp/intake/local_clickhouse.py
sed -n '480,555p' services/intake/tests/test_local_clickhouse.py
sed -n '730,825p' packages/nemo_platform_ext/src/nemo_platform_ext/cli/commands/services/cli.py

echo '== All reset-data references, excluding Git internals =='
rg -n -i -C 3 --glob '!**/.git/**' \
  'reset-data|reset_data|reset services data|reset services-data' . || true

echo '== Discover possible duplicate service CLI implementations =='
fd -t f 'cli.py' . | rg '/cli/commands/services/cli\.py$|/commands/services/cli\.py$' || true

echo '== Test files changed by target commit =='
git diff-tree --no-commit-id --name-only -r 4abb23e893 | rg '(^|/)(test|tests)(/|_)|test_.*\.py$' || true

Length of output: 75592


@BrianNewsom No actionable issue found.

reset-data is removed from the CLI, tests, and documentation. rm and prune remain limited to stopped instance directories and logs.

The Intake --remove path restores ownership only for ClickHouse data under the NeMo platform data directory. It preserves externally configured data outside that directory.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Addressed the compatibility review in 44f7fc0. The legacy wrapper now forwards arguments and selects legacy mode explicitly; direct module execution defaults to managed naming. Script-backed platform callers pin NMP_INTAKE_CLICKHOUSE_URL, wrapper cleanup targets its actual data directory, unmanaged credential diagnostics are accurate, and Insights resolves managed containers by label without guessing across multiple instances.

Validation: 340 broader unit tests plus 14 eval-stack tests and 1 real-Docker provisioning integration pass; focused Ruff, ty, shell syntax, and all applicable pre-commit hooks pass.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --no-patch --format='%H%n%s%n%b' 44f7fc0475

echo '== Changed files and patch =='
git show --stat --oneline --no-renames 44f7fc0475
git show --format= --no-ext-diff --unified=100 --no-renames 44f7fc0475

echo '== Relevant implementation/test maps =='
fd -t f . | rg '(local_clickhouse|run_clickhouse|insights|clickhouse).*\.py$|run_clickhouse\.sh$' | sort
ast-grep outline services/intake/src/nmp/intake/local_clickhouse.py --items all --type function,class
ast-grep outline services/intake/tests/test_local_clickhouse.py --items all --type function,class

echo '== Compatibility call sites, environment pins, labels, and cleanup =='
rg -n -C 8 --glob '*.py' --glob '*.sh' --glob '*.md' \
  'run_clickhouse|local_clickhouse|NMP_INTAKE_CLICKHOUSE_URL|--legacy|legacy.*mode|remove_local_clickhouse|credential|label' \
  services plugins packages e2e 2>/dev/null || true

Length of output: 201525


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Files changed by 44f7fc0475 =='
git diff-tree --no-commit-id --name-status -r 44f7fc0475

echo '== Focused patch =='
git show --format= --no-ext-diff --unified=60 --no-renames 44f7fc0475 -- \
  services/intake/src/nmp/intake/local_clickhouse.py \
  services/intake/scripts/spans/run_clickhouse.sh \
  services/intake/tests/test_local_clickhouse.py \
  services/intake/tests/integration/test_local_clickhouse_provisioning.py \
  plugins/nemo-insights \
  packages/nemo_platform_ext \
  sdk/python/nemo-platform

echo '== Current local ClickHouse symbols =='
rg -n --glob '*.py' \
  '^(async )?def |^class |^(LEGACY|MANAGED|CLICKHOUSE)_[A-Z_]+\s*=' \
  services/intake/src/nmp/intake/local_clickhouse.py

echo '== Current compatibility call sites and tests =='
rg -n -C 5 --glob '*.py' --glob '*.sh' \
  'run_clickhouse|NMP_INTAKE_CLICKHOUSE_URL|legacy_mode|legacy.*name|remove_local_clickhouse|managed.*label|containers\.list' \
  services/intake plugins/nemo-insights packages/nemo_platform_ext sdk/python/nemo-platform \
  -g '!**/.git/**' 2>/dev/null || true

Length of output: 139976


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Provisioning and removal implementation =='
sed -n '72,210p' services/intake/src/nmp/intake/local_clickhouse.py
sed -n '535,575p' services/intake/src/nmp/intake/local_clickhouse.py

echo '== All wrapper callers =='
rg -n -C 10 --glob '*.py' --glob '*.sh' \
  'scripts/spans/run_clickhouse\.sh|run_clickhouse\.sh' \
  services plugins packages e2e .github 2>/dev/null || true

echo '== Changed evaluator integration test =='
git show --format= --no-ext-diff --unified=80 44f7fc0475 -- \
  plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py

echo '== Current Insights resolver and cleanup tests =='
sed -n '475,555p' plugins/nemo-insights/testbed/reingest.py
sed -n '1215,1275p' plugins/nemo-insights/tests/testbed/test_reingest.py

echo '== Current wrapper documentation =='
rg -n -C 6 --glob '*.md' --glob '*.sh' \
  'legacy-script-mode|run_clickhouse\.sh|--remove|--data-dir' \
  services/intake/README.md services/intake/scripts/spans/run_clickhouse.sh

Length of output: 38866


@BrianNewsom No actionable issue found.

The wrapper forwards arguments and uses legacy mode. Direct module execution uses managed mode.

Script-backed platform callers pin NMP_INTAKE_CLICKHOUSE_URL. Wrapper cleanup uses the wrapper data directory.

Insights resolves one managed container by label and rejects multiple matches. The focused tests cover these paths.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

🤖 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 `@plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py`:
- Around line 101-115: Update the ClickHouse fixture setup around _wait_for_tcp
so the startup subprocess.run is inside the try block, ensuring teardown
executes even when provisioning or readiness fails. Change the cleanup
subprocess.run invocation to check=True so teardown failures propagate instead
of being suppressed.

In `@plugins/nemo-insights/testbed/reingest.py`:
- Line 513: Update the cleanup logic using running_names and
_LEGACY_CLICKHOUSE_CONTAINER to validate that the matched container has the
expected data mount or durable Intake identity before removing it; if validation
fails, preserve all state. Add a test covering an unrelated same-name running
container and verify it is not cleaned up.
- Around line 530-531: Bind the TTL command at lines 530-531 and cleanup at
lines 872-874 in reingest.py to an explicit verified target proving Intake uses
the managed local ClickHouse container, rather than relying on loopback
base_url; when binding is unknown or NMP_INTAKE_CLICKHOUSE_URL points
externally, skip both operations and preserve rows and workspace records. Add a
test covering loopback Intake with an external ClickHouse target.
🪄 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: 6586ebb4-9075-4eb8-9c68-97a3cb2c717f

📥 Commits

Reviewing files that changed from the base of the PR and between c99eced and 44f7fc0.

⛔ Files ignored due to path filters (1)
  • sdk/python/nemo-platform/src/nemo_platform/skills/inference/SKILL.md is excluded by !sdk/**
📒 Files selected for processing (12)
  • SETUP.md
  • packages/nemo_platform_ext/src/nemo_platform_ext/skills/inference/SKILL.md
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-insights/scripts/insights_demo.py
  • plugins/nemo-insights/testbed/eval/stack.py
  • plugins/nemo-insights/testbed/reingest.py
  • plugins/nemo-insights/tests/testbed/test_reingest.py
  • services/intake/README.md
  • services/intake/scripts/spans/run_clickhouse.sh
  • services/intake/src/nmp/intake/local_clickhouse.py
  • services/intake/tests/integration/test_local_clickhouse_provisioning.py
  • services/intake/tests/test_local_clickhouse.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • services/intake/README.md
  • services/intake/tests/integration/test_local_clickhouse_provisioning.py
  • SETUP.md
  • services/intake/src/nmp/intake/local_clickhouse.py

Comment thread plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py Outdated
Comment thread plugins/nemo-insights/testbed/reingest.py Outdated
Comment thread plugins/nemo-insights/testbed/reingest.py Outdated
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Addressed all three findings in 49d4b1c. Evaluator teardown is unconditional and loud. Insights Docker mutations now require an explicit local ClickHouse URL bound to exactly one Intake-labeled container on the matching host port; missing, external, ambiguous, and same-name/unlabeled targets preserve all state.

Validation: all 95 reingest tests, focused Ruff, ty, and format checks pass.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit="49d4b1c633"

echo '== PR head and target commit =='
gh pr view 1113 --repo NVIDIA-NeMo/nemo-platform --json headRefOid --jq '.headRefOid'
git show --no-patch --format='%H%n%s%n%b' "$commit"

echo '== Changed files =='
git diff-tree --no-commit-id --name-status -r "$commit"

echo '== Target patch =='
git show --format= --no-ext-diff --unified=100 --no-renames "$commit"

echo '== Candidate files =='
fd -t f . | rg '(^|/)(reingest|test_reingest|test_publish_to_intake|run_clickhouse|local_clickhouse).*\.py$' | sort

echo '== Source and test symbol map =='
ast-grep outline plugins/nemo-insights/testbed/reingest.py --items all --type function,class
ast-grep outline plugins/nemo-insights/tests/testbed/test_reingest.py --items all --type function,class
ast-grep outline plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py --items all --type function,class

echo '== Docker mutations, URL parsing, container selection, and teardown =='
rg -n -C 10 --glob '*.py' \
  'docker|containers\.(get|list)|exec_run|remove\(|stop\(|start\(|kill\(|NMP_INTAKE_CLICKHOUSE_URL|clickhouse.*url|url.*clickhouse|hostname|host.*port|port.*host|label|teardown|finalizer' \
  plugins/nemo-insights/testbed/reingest.py \
  plugins/nemo-insights/tests/testbed/test_reingest.py \
  plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py

Length of output: 139289


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== All Docker command construction in Insights testbed =='
rg -n -C 6 --glob '*.py' \
  '["'\'']docker["'\'']|docker\s+(exec|ps|rm|stop|start)|subprocess\.(run|Popen)\(' \
  plugins/nemo-insights/testbed plugins/nemo-insights/tests/testbed

echo '== Guarded resolver and cleanup call sites =='
rg -n -C 6 --glob '*.py' \
  '\b_local_clickhouse_container\s*\(|\b_stop_local_ttl_merges\s*\(|\bcleanup_scratch\s*\(' \
  plugins/nemo-insights

echo '== Removed container parameters or legacy-name references =='
rg -n -C 4 --glob '*.py' \
  'cleanup_scratch\([^)]*container|_stop_local_ttl_merges\([^)]*container|_LEGACY_CLICKHOUSE_CONTAINER|nmp-intake-clickhouse' \
  plugins/nemo-insights

echo '== Exact current changed regions =='
sed -n '485,558p;850,920p' plugins/nemo-insights/testbed/reingest.py
sed -n '90,118p' plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py

Length of output: 63596


@BrianNewsom One actionable issue remains.

plugins/nemo-insights/testbed/eval/stack.py, Line 62, still runs:

["docker", "exec", "nmp-intake-clickhouse", "clickhouse-client", "--query", "SYSTEM STOP TTL MERGES"]

This bypasses the new target verification in reingest.py. A same-name container can receive the mutation without checks for NMP_INTAKE_CLICKHOUSE_URL, Intake ownership labels, or the configured host port.

Use the guarded resolver, or verify the stack-owned container with the same URL, label, and port criteria before docker exec. Add coverage that a same-name unlabeled container is not mutated.

You are interacting with an AI system.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Fixed the remaining stack bypass in 1c94706. The stack now resolves the ClickHouse container using its configured local URL, Intake ownership labels, and published host port before issuing SYSTEM STOP TTL MERGES. It no longer executes against the historical name. Coverage verifies a same-name unlabeled container is excluded.

Validation: 111 combined Insights tests, focused Ruff, ty, and format checks pass.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

commit="1c94706c2f"

echo '== Commit and patch =='
git show --no-patch --format='%H%n%s%n%b' "$commit"
git diff-tree --no-commit-id --name-status -r "$commit"
git show --format= --no-ext-diff --unified=100 --no-renames "$commit"

echo '== Relevant source/test maps =='
ast-grep outline plugins/nemo-insights/testbed/eval/stack.py --items all --type function,class
ast-grep outline plugins/nemo-insights/testbed/reingest.py --items all --type function,class
ast-grep outline plugins/nemo-insights/tests/testbed/test_reingest.py --items all --type function,class
fd -t f . plugins/nemo-insights | rg '(^|/)(test_.*stack|stack.*test|test_reingest).*\.py$' || true

echo '== Resolver, Docker mutation, URL, label, and port references =='
rg -n -C 10 --glob '*.py' \
  'SYSTEM STOP TTL MERGES|exec_run|docker.*exec|_local_clickhouse_container|NMP_INTAKE_CLICKHOUSE_URL|clickhouse.*url|url.*clickhouse|containers\.(get|list)|label|host.*port|port.*host|nmp-intake-clickhouse' \
  plugins/nemo-insights/testbed \
  plugins/nemo-insights/tests

echo '== Targeted current source regions =='
sed -n '1,150p' plugins/nemo-insights/testbed/eval/stack.py
sed -n '470,570p' plugins/nemo-insights/testbed/reingest.py

Length of output: 120686


@BrianNewsom No actionable issue found.

stack.py resolves exactly one running container with both Intake ownership labels and the configured local URL port before it runs SYSTEM STOP TTL MERGES.

A same-name unlabeled container does not match the resolver. The regression test covers this case.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@BrianNewsom

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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)
plugins/nemo-insights/tests/testbed/test_eval_scripts.py (1)

15-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise the configured-port and rejection paths.

The success test calls _intake_clickhouse_container() without clickhouse_url, so it verifies only the default port 8123. It does not verify the new URL-to-port conversion. Add focused cases for a non-default local URL, a non-local URL, and multiple matching containers.

Example success-case adjustment
-    assert stack._intake_clickhouse_container() == "nmp-intake-clickhouse-managed"
+    assert stack._intake_clickhouse_container("http://localhost:55123") == "nmp-intake-clickhouse-managed"
...
-    assert "publish=8123" in commands[0]
+    assert "publish=55123" in commands[0]
🤖 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 `@plugins/nemo-insights/tests/testbed/test_eval_scripts.py` around lines 15 -
47, Extend the tests for _intake_clickhouse_container to pass a non-default
local clickhouse_url and assert the Docker filter uses its converted port, add a
non-local URL case that verifies the expected rejection or fallback behavior,
and add a multiple-matching-containers case to verify the selection behavior.
Preserve the existing label assertions and unlabeled-container rejection
coverage.
🤖 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 `@plugins/nemo-insights/testbed/eval/stack.py`:
- Around line 108-112: Update the subprocess environment setup around the
ClickHouse startup invocation and its delegated provisioner so both subprocesses
receive NMP_INTAKE_CLICKHOUSE_URL set to CLICKHOUSE_URL. Ensure
run_clickhouse.sh is pinned to the local ClickHouse endpoint rather than relying
on legacy-script-mode behavior.

---

Nitpick comments:
In `@plugins/nemo-insights/tests/testbed/test_eval_scripts.py`:
- Around line 15-47: Extend the tests for _intake_clickhouse_container to pass a
non-default local clickhouse_url and assert the Docker filter uses its converted
port, add a non-local URL case that verifies the expected rejection or fallback
behavior, and add a multiple-matching-containers case to verify the selection
behavior. Preserve the existing label assertions and unlabeled-container
rejection coverage.
🪄 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: 995b20bb-6b68-4e5c-bca5-7d51eab14b17

📥 Commits

Reviewing files that changed from the base of the PR and between 44f7fc0 and 1c94706.

📒 Files selected for processing (5)
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-insights/testbed/eval/stack.py
  • plugins/nemo-insights/testbed/reingest.py
  • plugins/nemo-insights/tests/testbed/test_eval_scripts.py
  • plugins/nemo-insights/tests/testbed/test_reingest.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • plugins/nemo-evaluator/tests/integration/test_publish_to_intake.py
  • plugins/nemo-insights/tests/testbed/test_reingest.py
  • plugins/nemo-insights/testbed/reingest.py

Comment thread plugins/nemo-insights/testbed/eval/stack.py
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom

Copy link
Copy Markdown
Contributor Author

Manual local E2E verification:

# From an existing nemo-platform clone. Run make bootstrap-python first if needed.
gh pr checkout 1113
docker info >/dev/null

uv run nemo services start \
  --services auth,entities,intake \
  --instance clickhouse-pr-e2e

# Writes an OTLP trace and polls Intake until it reads sample-session back.
uv run services/intake/examples/send_otel_sample.py \
  --endpoint http://127.0.0.1:8080/apis/intake/v2/workspaces/default/ingest/otlp/v1/traces

# Show the two persisted ClickHouse-backed rows explicitly.
curl -fsSG \
  --data-urlencode 'filter[session_id]=sample-session' \
  http://127.0.0.1:8080/apis/intake/v2/workspaces/default/spans \
  | jq '.data[] | {name, session_id, source}'

uv run nemo services stop --instance clickhouse-pr-e2e

Expected readback includes sample-chain and sample-llm, both with session_id: sample-session and source: otel. Docker must already be running; if docker info fails, start Docker Desktop (macOS/Windows) or the Docker service (Linux) and retry.

I ran this exact sequence against the current PR head. The smoke script completed its built-in readback, the explicit query returned both rows, and graceful service shutdown stopped the managed ClickHouse container without deleting its data.

@ironcommit ironcommit 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, would like further clean up at some point.

@BrianNewsom
BrianNewsom enabled auto-merge August 6, 2026 22:24
@BrianNewsom
BrianNewsom added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 4f05a30 Aug 7, 2026
59 checks passed
@BrianNewsom
BrianNewsom deleted the clickhouse-local-provisioning/brnewsom branch August 7, 2026 03: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.

3 participants