Skip to content

Commit f708a36

Browse files
authored
fix(e2e): load DCode model selector through tsx (#9688)
<!-- markdownlint-disable MD041 --> ## Summary The LangChain Deep Agents Code fresh onboarding E2E check stopped before model selection because its `.mts` command used named imports from CommonJS project modules. This change loads those modules through the boundary that both standalone `tsx` and Vitest expose. The existing source test did not detect the defect because Vitest transformed the imports. A subprocess regression test now runs the same `tsx` command that the live E2E check uses. ## Changes - Load the URL, onboarding-probe, and provider-model exports through namespace imports and their CommonJS `default` export when required. - Add a local module-unwrapping helper because standalone `tsx` and Vitest expose the same project modules differently. The fresh-onboarding selector is the current consumer, and the subprocess regression test protects both execution paths. - Run the selector without a credential in the regression test and require the expected credential error after module initialization. ## Type of Change - [x] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [ ] Doc only (includes code sample changes) ## Quality Gates - [x] Tests added or updated for changed behavior - [ ] Existing tests cover changed behavior — justification: - [ ] Tests not applicable — justification: - [x] Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging) - [x] Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: The change does not alter credential custody, endpoint validation, network requests, or sandbox state. The regression test supplies no credential and confirms that validation rejects the missing value after module initialization. - [ ] Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue: ## DGX Station Hardware Evidence - [ ] Tested on DGX Station - Tested commit: - Station profile/scenario: - Result: - Supporting evidence: ## Verification - [x] PR description includes a `Signed-off-by:` line and every commit appears as `Verified` in GitHub - [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or `npm run validate:pr` passed after refreshing `origin/main` when hooks were skipped or unavailable - [x] Targeted behavior tests pass for the current change set, or tests are marked not applicable above — `npx vitest run --project e2e-support test/e2e/support/authorized-chat-model-selection.test.ts` passed 6 tests; `npm run checks:repository` passed. - [ ] Applicable broad gate passed — `npm test` for broad runtime/test-harness changes; `npm run check` for repo-wide validation/coverage changes — command/result: - [x] Quality Gates section completed with required justifications or waivers - [x] No secrets, API keys, or credentials committed - [ ] `npm run docs` builds without warnings (doc changes only) - [ ] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new docs only) --- Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved chat-model selection compatibility across supported module formats. * Chat-model selection now reports unsafe non-secure connections with a clear error and exits with an appropriate failure status. * **Tests** * Added end-to-end coverage validating behavior when a credential is provided but the service uses an unsupported non-loopback HTTP endpoint. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Prekshi Vyas <prekshiv@nvidia.com>
1 parent bf458dc commit f708a36

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

test/e2e/lib/select-authorized-chat-model.mts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { probeOpenAiLikeEndpointOptimized } from "../../../src/lib/inference/onboard-probes.ts";
5-
import { fetchOpenAiLikeModels } from "../../../src/lib/inference/provider-models.ts";
6-
import { isLoopbackHostname } from "../../../src/lib/core/url-utils.ts";
4+
import * as urlUtilsModule from "../../../src/lib/core/url-utils.ts";
5+
import * as onboardProbesModule from "../../../src/lib/inference/onboard-probes.ts";
6+
import * as providerModelsModule from "../../../src/lib/inference/provider-models.ts";
77
import type { ModelCatalogFetchResult } from "../../../src/lib/onboard/types.ts";
88

9+
function unwrapCommonJsModule<T extends object>(module: T): T {
10+
return (module as T & { default?: T }).default ?? module;
11+
}
12+
13+
const { isLoopbackHostname } = unwrapCommonJsModule(urlUtilsModule);
14+
const { probeOpenAiLikeEndpointOptimized } = unwrapCommonJsModule(onboardProbesModule);
15+
const { fetchOpenAiLikeModels } = unwrapCommonJsModule(providerModelsModule);
16+
917
const CHAT_MODEL_HINT = /(?:claude|deepseek|gemma|gpt|kimi|llama|mistral|nemotron|phi|qwen)/iu;
1018
const NON_CHAT_MODEL_HINT =
1119
/(?:audio|clip|embed|guard|image|moderation|ocr|rerank|retrieval|reward|safety|speech|video)/iu;

test/e2e/support/authorized-chat-model-selection.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,44 @@
11
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { spawnSync } from "node:child_process";
5+
import path from "node:path";
6+
47
import { describe, expect, it, vi } from "vitest";
58

69
import { selectAuthorizedChatModel } from "../lib/select-authorized-chat-model.mts";
710

811
const endpoint = "https://inference.example.test/v1";
912
const currentModel = "nvidia/nvidia/nemotron-3-ultra";
13+
const selectorPath = path.resolve("test/e2e/lib/select-authorized-chat-model.mts");
14+
const tsxPath = path.resolve("node_modules/.bin/tsx");
1015

1116
describe("authorized alternate chat model selection", () => {
17+
it("rejects unsafe credential transport when tsx executes the selector", () => {
18+
const result = spawnSync(
19+
tsxPath,
20+
[
21+
selectorPath,
22+
"--endpoint",
23+
"http://inference.example.test/v1",
24+
"--current-model",
25+
currentModel,
26+
],
27+
{
28+
encoding: "utf8",
29+
env: { ...process.env, COMPATIBLE_API_KEY: "placeholder-key" },
30+
killSignal: "SIGKILL",
31+
timeout: 10_000,
32+
},
33+
);
34+
35+
expect(result.error).toBeUndefined();
36+
expect(result.status).toBe(1);
37+
expect(result.stderr).toContain(
38+
"authorized model selection failed: the endpoint must use HTTPS unless it targets loopback",
39+
);
40+
});
41+
1242
it.each([endpoint, "http://127.0.0.1:8000/v1"])(
1343
"selects the highest-priority catalog model at %s",
1444
async (permittedEndpoint) => {

0 commit comments

Comments
 (0)