Skip to content

fix: keep NVIDIA NIM availability validation fail-open - #13

Merged
jyje merged 1 commit into
feat/nvidia-model-availability-validationfrom
fix/nvidia-nim-availability-fail-open
Aug 14, 2026
Merged

fix: keep NVIDIA NIM availability validation fail-open#13
jyje merged 1 commit into
feat/nvidia-model-availability-validationfrom
fix/nvidia-nim-availability-fail-open

Conversation

@jyje

@jyje jyje commented Aug 14, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up review fixes for feat/nvidia-model-availability-validation (upstream PR langchain-ai#492). Targets that branch, not main.

unavailable is a hard stop. resolveRunConfig throws, the error is tagged config, and the run aborts before inference with no bypass env var. That makes every inconclusive catalogue lookup that resolves to unavailable a total blocker, which is the opposite of what the feature intends. Four paths could reach it without any evidence that the model cannot be invoked.

What changed

Custom endpoint detection compares URLs, not env presence. baseUrlIsCustom was derived from NVIDIA_BASE_URL merely being set. The provider's built-in default is already https://integrate.api.nvidia.com/v1, so a user who pins that exact value (from a shared .env template, a compose file, or just being explicit after reading the README's "alternative base URLs" note) flipped the flag to true and made the hosted catalogue authoritative. That catalogue is a public listing, not an entitlement list, so a model the key can actually invoke but that is absent from the listing hard-failed the run.

The new providerBaseUrlIsCustom helper in src/config/constants.ts compares the resolved base URL against ProviderConfig.baseURL, normalizing whitespace and trailing slashes. It also removes the duplicated override-detection logic that had been reimplemented at the call site, so it can no longer drift from resolveProviderBaseUrl.

An empty catalogue is unknown, not unavailable. Array.isArray([]) is true, so {"object":"list","data":[]} fell straight through .some() into the hard-block path. The README documents NVIDIA_BASE_URL for a self-hosted or proxied gateway, and proxies such as LiteLLM or a KServe router commonly return an empty or filtered /v1/models when the key lacks list scope while inference still works fine.

The lookup is bounded by a 5s timeout. The fetch had no AbortSignal. Previously it only ever contacted api.openai.com; this feature points it at arbitrary user-supplied infrastructure. A self-hosted NIM behind a VPN that accepts the connection then stalls held every run before inference for undici's 300s default. Uses the same AbortSignal.timeout pattern as src/connectors/http.ts.

The catalogue URL is built from pathname. new URL("models", ensureTrailingSlash(base)) dropped the last path segment when the base URL carried a query string or fragment.

https://nim.example/v1?api-version=2024  ->  https://nim.example/models        (was)
https://nim.example/v1?api-version=2024  ->  https://nim.example/v1/models?api-version=2024  (now)

Usually that 404s and degrades to unknown, so validation silently stopped working. But if the wrong path happened to serve a different catalogue, it produced a false unavailable. URL construction also moved inside a guard, so a base URL that cannot be parsed returns unknown instead of throwing out of the availability check.

The thrown error carries the real reason. It previously blamed "the configured credentials" for a model that simply is not loaded on the endpoint, and discarded the reason field that said so accurately.

Validation

  • pnpm run format:check
  • pnpm run lint:check
  • pnpm run typecheck
  • pnpm vitest run test/model-availability.test.ts test/config/constants.test.ts — 94 passed

Nine new tests cover each fix: the default-value-pinned base URL, the empty catalogue, the query-string base URL, the abort signal, and the providerBaseUrlIsCustom helper across default, restated-default, custom, no-default, and whitespace-only inputs.

The full suite has 13 pre-existing failing files on this machine (parallel-load timeouts in evals/ledger/*, test/agent/*, test/connectors/sources/git-repo.test.ts). Verified as baseline: the same 13 files fail on the parent commit with these changes stashed (31 failed tests before, 29 after, with 9 tests added). Every one of them passes when run in isolation.

Reviewer reproduction

pnpm install --frozen-lockfile
pnpm vitest run test/model-availability.test.ts test/config/constants.test.ts

To confirm the main regression by hand, set NVIDIA_BASE_URL to the documented hosted endpoint and check that validation stays non-authoritative:

export OPENWIKI_PROVIDER=nvidia
export NVIDIA_API_KEY="<your-key>"
export NVIDIA_BASE_URL="https://integrate.api.nvidia.com/v1"

pnpm dev -- --debug -p "Reply with OK."

Expected: model.availability=unknown reason=The NVIDIA hosted endpoint is not validated. in debug output, and the run proceeds. Before this change, the hosted catalogue was queried and could block the run.

`unavailable` aborts the run before inference with no override, so every
inconclusive catalogue lookup that resolves to it is a total blocker. Four
paths could reach it without evidence that the model cannot be invoked.

- Derive "custom endpoint" by comparing the resolved base URL against the
  provider's built-in default rather than testing whether the env var is
  set. Pinning `NVIDIA_BASE_URL` to the documented hosted endpoint made the
  public hosted catalogue authoritative over entitlement it does not model.
- Treat an empty `data` array as `unknown`. A proxied gateway that hides its
  catalogue from a key without list scope still serves inference.
- Bound the lookup with a 5s `AbortSignal.timeout`. The request now targets
  user-supplied infrastructure, where a stalled host held every run for
  undici's 300s default.
- Build the catalogue URL from `pathname` so a base URL carrying a query
  string keeps its last path segment, and fall back to `unknown` if the URL
  cannot be resolved at all.

Also surface the availability reason in the thrown error, which previously
blamed credentials for a model simply not loaded on the endpoint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jyje
jyje merged commit 340fbd8 into feat/nvidia-model-availability-validation Aug 14, 2026
8 checks passed
jyje added a commit that referenced this pull request Aug 29, 2026
`unavailable` aborts the run before inference with no override, so every
inconclusive catalogue lookup that resolves to it is a total blocker. Four
paths could reach it without evidence that the model cannot be invoked.

- Derive "custom endpoint" by comparing the resolved base URL against the
  provider's built-in default rather than testing whether the env var is
  set. Pinning `NVIDIA_BASE_URL` to the documented hosted endpoint made the
  public hosted catalogue authoritative over entitlement it does not model.
- Treat an empty `data` array as `unknown`. A proxied gateway that hides its
  catalogue from a key without list scope still serves inference.
- Bound the lookup with a 5s `AbortSignal.timeout`. The request now targets
  user-supplied infrastructure, where a stalled host held every run for
  undici's 300s default.
- Build the catalogue URL from `pathname` so a base URL carrying a query
  string keeps its last path segment, and fall back to `unknown` if the URL
  cannot be resolved at all.

Also surface the availability reason in the thrown error, which previously
blamed credentials for a model simply not loaded on the endpoint.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant