Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/get-started/quickstart-hermes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ Use these details when your first-run path needs more control.
```bash
nemohermes inference set --model <model> --provider <provider>
```

If the in-sandbox config write or integrity hash update fails, `nemohermes inference set` exits with status `1` after committing the OpenShell route and NemoClaw registry.
Run the printed `nemohermes <sandbox-name> rebuild` command to make the in-sandbox Hermes configuration match the committed route.
</Accordion>
</AccordionGroup>

Expand Down
15 changes: 15 additions & 0 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4094,8 +4094,23 @@ NemoClaw resolves the OpenShell gateway from the target sandbox's recorded gatew
Do not run `openshell inference set` directly on a shared NemoClaw gateway because that bypasses registry compatibility checks and can break other sandboxes.
When either flag is missing, `$$nemoclaw inference set` reports both required flags without suggesting a raw OpenShell command.
The command updates the host registry immediately after the gateway route changes.

</AgentOnly>

<AgentOnly variant="openclaw">

If the in-sandbox config sync fails, NemoClaw keeps the gateway and registry aligned, warns that the running image may still need a rebuild, and points you to `$$nemoclaw <name> rebuild`.

</AgentOnly>

<AgentOnly variant="hermes">

If the in-sandbox config write or integrity hash update fails, the OpenShell route and NemoClaw registry remain committed, but the command exits with status `1` and points you to `$$nemoclaw <name> rebuild`.

</AgentOnly>

<AgentOnly variant="openclaw,hermes">

Supported provider names are `nvidia-prod`, `nvidia-nim`, `nvidia-router`, `openai-api`, `anthropic-prod`, `compatible-anthropic-endpoint`, `gemini-api`, `compatible-endpoint`, `hermes-provider`, `ollama-local`, and `vllm-local`.
Use `--no-verify` only when OpenShell cannot verify the provider at switch time but you have already confirmed the provider and credential.
When switching to `compatible-endpoint` or `compatible-anthropic-endpoint` from a different provider family, pass `--endpoint-url` with the trusted custom provider URL and, except for the Hermes case below, `--inference-api` with its API family so NemoClaw can persist a complete route identity for rebuild and shared-gateway checks.
Expand Down
65 changes: 46 additions & 19 deletions src/lib/actions/inference-set-hermes-run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe("runInferenceSet Hermes routing", () => {
expect(seedOrder).toBeGreaterThan(writeOrder);
});

it("does not re-seed the dashboard when the in-sandbox config write fails (#6893)", async () => {
it("fails after commit when the in-sandbox config write fails (#7083)", async () => {
const config: ConfigObject = {
model: { default: "moonshotai/kimi-k2.6", provider: "custom" },
};
Expand All @@ -227,22 +227,36 @@ describe("runInferenceSet Hermes routing", () => {
throw new Error("write failed");
});

await runInferenceSet(
{
await expect(
runInferenceSet(
{
provider: "hermes-provider",
model: "openai/gpt-5.4-mini",
sandboxName: "hermes",
noVerify: true,
},
deps,
),
).rejects.toMatchObject({
name: "InferenceSetError",
exitCode: 1,
message: expect.stringMatching(/Hermes inference route synchronization did not complete/),
});

// A failed in-sandbox Hermes config write leaves the old config in place; re-seeding the
// dashboard from it would be pointless. The host route remains committed, but
// the command must fail so automation cannot accept a partial switch.
expect(deps.calls.updateSandbox).toHaveBeenCalledWith(
"hermes",
expect.objectContaining({
provider: "hermes-provider",
model: "openai/gpt-5.4-mini",
sandboxName: "hermes",
noVerify: true,
},
deps,
}),
);

// A failed gateway-config write leaves the old config in place; re-seeding the
// dashboard from it would be pointless (and the guidance is to rebuild).
expect(deps.calls.seedHermesDashboardConfig).not.toHaveBeenCalled();
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it("does not re-seed or report synced when the config hash refresh fails (#6893)", async () => {
it("fails after commit when the config hash refresh fails (#7083)", async () => {
const config: ConfigObject = {
model: { default: "moonshotai/kimi-k2.6", provider: "custom" },
};
Expand All @@ -262,17 +276,30 @@ describe("runInferenceSet Hermes routing", () => {
throw new Error("hash refresh failed");
});

await runInferenceSet(
{
await expect(
runInferenceSet(
{
provider: "hermes-provider",
model: "openai/gpt-5.4-mini",
sandboxName: "hermes",
noVerify: true,
},
deps,
),
).rejects.toMatchObject({
name: "InferenceSetError",
exitCode: 1,
message: expect.stringMatching(/Hermes inference route synchronization did not complete/),
});

expect(deps.calls.writeSandboxConfig).toHaveBeenCalledOnce();
expect(deps.calls.updateSandbox).toHaveBeenCalledWith(
"hermes",
expect.objectContaining({
provider: "hermes-provider",
model: "openai/gpt-5.4-mini",
sandboxName: "hermes",
noVerify: true,
},
deps,
}),
);

expect(deps.calls.writeSandboxConfig).toHaveBeenCalledOnce();
expect(deps.calls.seedHermesDashboardConfig).not.toHaveBeenCalled();
const logs = deps.calls.log.mock.calls.map((call) => String(call[0]));
expect(logs.some((line) => line.includes("failed to refresh its integrity hash"))).toBe(true);
Expand Down
15 changes: 12 additions & 3 deletions src/lib/actions/inference-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1239,8 +1239,9 @@ async function runInferenceSetWithoutHostLock(
? ` Syncing Hermes model route in sandbox '${sandboxName}'...`
: ` Syncing OpenClaw model identity in sandbox '${sandboxName}'...`,
);
// In-sandbox config is the last, crash-prone layer (gateway + registry already consistent):
// - don't abort on failure; track whether it synced, never report a false "synced"
// In-sandbox config is the last, crash-prone layer (gateway + registry already consistent).
// OpenClaw keeps its existing degraded result on failure. Hermes finalizes the committed
// route and registry, then returns an error so automation cannot accept partial convergence.
// Two degraded states, both fixed by `rebuild` (regenerates openclaw.json + .config-hash from registry):
// - write fails: config left old (old .config-hash still matches it)
// - hash recompute fails: config new but .config-hash stale -> integrity-guard mismatch
Expand Down Expand Up @@ -1299,7 +1300,7 @@ async function runInferenceSetWithoutHostLock(
reasoningEffortRequest,
);

return finalizeInferenceMutation(
const mutation = finalizeInferenceMutation(
{
agentName,
configChanged: patched.changed,
Expand All @@ -1319,6 +1320,14 @@ async function runInferenceSetWithoutHostLock(
},
deps,
);
if (agentName === "hermes" && !inSandboxConfigSynced) {
throw new InferenceSetError(
`Hermes inference route synchronization did not complete for '${sandboxName}'. ` +
`The OpenShell route and NemoClaw registry remain committed, but the in-sandbox ` +
`Hermes configuration did not fully converge. Run '${CLI_NAME} ${sandboxName} rebuild' to converge it.`,
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
return mutation;
} catch (error) {
if (!providerMutation) throw error;
if (restoredSelectionAfterProviderFailure) throw error;
Expand Down
Loading