Skip to content

fix(platform-runner): seed NMP_BASE_URL from config platform.base_url - #706

Merged
benmccown merged 2 commits into
mainfrom
base-url-bug
Jul 15, 2026
Merged

fix(platform-runner): seed NMP_BASE_URL from config platform.base_url#706
benmccown merged 2 commits into
mainfrom
base-url-bug

Conversation

@benmccown

@benmccown benmccown commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

What this touches

NMP_BASE_URL — the URL other components use to reach the platform's own API.
The platform injects it into deployed agents so they can call back through the
Inference Gateway; if it's wrong for a containerized agent, that agent can't
reach any models. It can be set three ways, resolved in this order:

NEMO_BASE_URL env  >  NMP_BASE_URL env  >  platform.base_url (config file)
  • NMP_BASE_URL env exists so a deployer (Helm/k8s) can point the platform
    at its real service address (e.g. http://nemo-platform-api:8080); it takes
    priority because the deployer knows the true address.
  • platform.base_url in config is the operator-facing equivalent for a
    self-hosted/standalone run — you'd set it to whatever address is reachable in
    your environment (e.g. the docker bridge http://172.17.0.1:8080 so
    containers can reach the host).

The bug

When you start the platform with nemo services run, the runner
(apply_run_environment) auto-fills NMP_BASE_URL for standalone users who
haven't set it — it derives a value from the server's own bind address:

connect_host  = _connect_host_for_internal_clients(config.host)  # 0.0.0.0 -> 127.0.0.1
default       = f"http://{connect_host}:{port}"                  # http://127.0.0.1:8080
env.setdefault("NMP_BASE_URL", default)

--host 0.0.0.0 means "bind all interfaces," which isn't itself a
connectable address, so _connect_host_for_internal_clients translates it to
the loopback 127.0.0.1 (fine for in-process clients on the same host). The
result — http://127.0.0.1:8080 — is written into NMP_BASE_URL via
setdefault.

The problem: NMP_BASE_URL (env) ranks above platform.base_url (config).
So the runner's auto-derived loopback value silently overrides whatever you
set as platform.base_url in your config — the config setting becomes a no-op,
with no error or warning. The only way to make it stick today is to also
export NMP_BASE_URL yourself before launch, which defeats the purpose of the
config field.

The user-visible symptom: an agent deployed in a container gets
llms.*.base_url = http://127.0.0.1:8080, which inside the container points at
the container itself, so its model calls fail. (Helm/k8s deployments are
unaffected — there NMP_BASE_URL is set externally to the real, reachable
address, so nothing is being shadowed.)

Fix

apply_run_environment now uses an explicit platform.base_url from the config
file as the default it seeds NMP_BASE_URL with (read from raw YAML so a
file-set value is distinguishable from the schema default), and only falls back
to the bind-derived loopback when the config doesn't set one. An externally
provided NMP_BASE_URL still wins via setdefault, so Helm/k8s is unchanged.

New precedence: external env > config platform.base_url > bind-derived default.

Testing

  • New unit tests in test_config.py: seeds from config, external env still
    wins, falls back when config omits base_url, falls back when the file is
    missing. Full runner suite green.
  • Verified end-to-end in a dev pod: with only platform.base_url set (no
    NMP_BASE_URL env), a docker-mode agent now deploys and invokes through the
    gateway (previously failed).

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Refined how runtime NMP_BASE_URL is derived: existing environment values are preserved; otherwise the configured platform.base_url supplies the host while the bind port is applied.
    • Improved handling of missing/malformed config, including IPv6 and wildcard hosts, and ensured NMP_AUTH_POLICY_DECISION_POINT_BASE_URL matches the final value.
  • Tests

    • Expanded coverage for these precedence and fallback behaviors, including config present/absent cases.

@benmccown
benmccown requested review from a team as code owners July 15, 2026 18:52
@github-actions github-actions Bot added the fix label Jul 15, 2026
@benmccown benmccown self-assigned this Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 00a52233-cf95-4156-98f7-0d63218211dc

📥 Commits

Reviewing files that changed from the base of the PR and between 26b1c63 and ee811b1.

📒 Files selected for processing (2)
  • packages/nmp_platform_runner/src/nmp/platform_runner/config.py
  • packages/nmp_platform_runner/tests/test_config.py

📝 Walkthrough

Walkthrough

Changes

Base URL Resolution

Layer / File(s) Summary
Base URL precedence and config parsing
packages/nmp_platform_runner/src/nmp/platform_runner/config.py
apply_run_environment prioritizes an existing NMP_BASE_URL, then valid YAML scheme/hostname with the effective bind port, and finally bind-derived defaults with IPv6 handling.
Base URL regression coverage
packages/nmp_platform_runner/tests/test_config.py
Tests cover configured hosts, port replacement, environment overrides, schemes, malformed IPv6, wildcard normalization, missing files, and bundled configuration behavior.

Suggested reviewers: ironcommit

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: seeding NMP_BASE_URL from config platform.base_url.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch base-url-bug

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

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 25477/32667 78.0% 62.6%
Integration Tests 14696/31316 46.9% 19.3%

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

e2e tests go brr?

Comment thread packages/nmp_platform_runner/src/nmp/platform_runner/config.py Outdated
…e_url

apply_run_environment derived NMP_BASE_URL from the bind host/port (a
loopback address in standalone mode) and set it via setdefault. Because
get_base_url() prefers the NMP_BASE_URL env var over the config file, an
explicit platform.base_url in the config was silently shadowed by the
runner-derived loopback value — making the setting a no-op.

This broke container-mode agent deployments: the gateway URL injected into
a deployed agent's NAT config (llms.*.base_url) resolved to 127.0.0.1,
which inside the agent container is the container itself, so the agent's
outbound model calls failed.

Seed NMP_BASE_URL from the *host* of an explicit platform.base_url in the
config file (read from the raw YAML so a file-set value is distinguishable
from the schema default), paired with the port the server actually binds.
Only the host is honored — a config that hardcodes a port (e.g. :8080) must
not point internal in-process clients (and the embedded PDP) at that port
when the platform is launched on a different one (which the e2e harness
always does, and any 'nemo services run --port' other than 8080 does);
using the config port there leaves internal HTTP clients unable to reach
the server, so the platform never becomes ready. An externally-provided
NMP_BASE_URL (Helm/k8s) still wins via setdefault, preserving deployed-mode
behavior.

Signed-off-by: Ben McCown <bmccown@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: 2

🧹 Nitpick comments (1)
packages/nmp_platform_runner/tests/test_config.py (1)

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

Annotate tmp_path as pathlib.Path.

Import Path normally and type each changed fixture parameter as tmp_path: Path.

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

Also applies to: 230-230, 238-238, 248-248, 254-254, 260-260

🤖 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/nmp_platform_runner/tests/test_config.py` at line 225, Import
pathlib.Path normally in the test module and update each affected fixture
parameter in _write_config and the other referenced test helpers to use the
concrete annotation tmp_path: Path. Replace any string-based or missing tmp_path
annotations while preserving the existing helper behavior.

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/nmp_platform_runner/src/nmp/platform_runner/config.py`:
- Around line 182-187: Update the base URL initialization around
_config_file_base_url_host to catch urlparse failures from malformed bracketed
IPv6 configuration values. On parse failure, fall back to the external
NMP_BASE_URL handling via env.setdefault instead of aborting startup, while
preserving the existing configured-host and url_host defaults for valid inputs.

In `@packages/nmp_platform_runner/tests/test_config.py`:
- Around line 230-252: The apply_run_environment URL construction currently
replaces configured schemes with http. Preserve the parsed scheme from the
configured platform base URL when generating NMP_BASE_URL and
NMP_AUTH_POLICY_DECISION_POINT_BASE_URL, including when applying the bind port;
add HTTPS coverage alongside the existing tests to verify https:// remains
unchanged.

---

Nitpick comments:
In `@packages/nmp_platform_runner/tests/test_config.py`:
- Line 225: Import pathlib.Path normally in the test module and update each
affected fixture parameter in _write_config and the other referenced test
helpers to use the concrete annotation tmp_path: Path. Replace any string-based
or missing tmp_path annotations while preserving the existing helper behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ab4841f5-1c43-489f-9dec-95655a832510

📥 Commits

Reviewing files that changed from the base of the PR and between 848e5fe and 26b1c63.

📒 Files selected for processing (2)
  • packages/nmp_platform_runner/src/nmp/platform_runner/config.py
  • packages/nmp_platform_runner/tests/test_config.py

Comment thread packages/nmp_platform_runner/src/nmp/platform_runner/config.py Outdated
Comment thread packages/nmp_platform_runner/tests/test_config.py Outdated

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

Non-blocking feedback from review — looks sufficient for the stated bug. Inline notes below are optional polish, not merge blockers.

Comment thread packages/nmp_platform_runner/src/nmp/platform_runner/config.py Outdated
Comment thread packages/nmp_platform_runner/src/nmp/platform_runner/config.py Outdated
Comment thread packages/nmp_platform_runner/src/nmp/platform_runner/config.py Outdated
Comment thread packages/nmp_platform_runner/tests/test_config.py
Comment thread packages/nmp_platform_runner/tests/test_config.py Outdated
@tylersbray

Copy link
Copy Markdown
Contributor

Non-blocking — documentation follow-up: SETUP.md still tells users to export NMP_BASE_URL=... without mentioning that platform.base_url now seeds NMP_BASE_URL when the env var is unset. A one-line note there would help operators discover the config-only path this PR enables. (Not in this diff, so filing here rather than inline.)

@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 approval, left some optional non-blockers.

…d base_url

Address PR review feedback on config-seeded NMP_BASE_URL:

- Preserve the configured scheme: a config platform.base_url of https://...
  is no longer downgraded to http:// (parse and carry scheme + host).
- Normalize the config-derived host through the same wildcard -> loopback
  translation as the bind host, so the bundled local.yaml default
  (http://0.0.0.0:8080) seeds a connectable http://127.0.0.1:<port> instead
  of the non-connectable wildcard, keeping internal PDP/readiness clients working.
- Guard urlparse against malformed bracketed IPv6 (e.g. http://[::1): fail
  soft to the bind-derived default instead of aborting startup.
- Correct the docstring to match behavior (scheme and host are honored).

Tests: add HTTPS scheme preservation, malformed-IPv6 fallback, wildcard
normalization, and a bundled default_config_path() regression guard; annotate
tmp_path fixtures as pathlib.Path.

Signed-off-by: Ben McCown <bmccown@nvidia.com>
@benmccown
benmccown added this pull request to the merge queue Jul 15, 2026
Merged via the queue into main with commit 25ed758 Jul 15, 2026
57 checks passed
@benmccown
benmccown deleted the base-url-bug branch July 15, 2026 21:40
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