forked from NVIDIA/NemoClaw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinference-switch-retry.ts
More file actions
53 lines (47 loc) · 2.03 KB
/
Copy pathinference-switch-retry.ts
File metadata and controls
53 lines (47 loc) · 2.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
import type { ShellProbeResult } from "./shell-probe.ts";
import { runBoundedRetry, type RetryEvidence } from "./retry-policy.ts";
const TRANSIENT_INFERENCE_SET_FAILURE =
/timed? out|timeout|ETIMEDOUT|ECONNRESET|EAI_AGAIN|ENOTFOUND|failed to connect|error sending request|\b50[234]\b/iu;
export function inferenceSetAttemptCount(raw: string | undefined, fallback = 3): number {
if (raw === undefined) return fallback;
const parsed = Number(raw);
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 10) {
throw new Error(`NEMOCLAW_SWITCH_SET_ATTEMPTS must be between 1 and 10; got ${raw}`);
}
return parsed;
}
export function isTransientInferenceSetFailure(result: ShellProbeResult): boolean {
return TRANSIENT_INFERENCE_SET_FAILURE.test(`${result.stdout}\n${result.stderr}`);
}
export function inferenceResponseModel(raw: string): string {
const response = JSON.parse(raw) as { model?: unknown };
return typeof response.model === "string" ? response.model : "";
}
export async function runInferenceSetWithRetry(options: {
attempts: number;
delay?: (milliseconds: number) => Promise<void>;
run: (attempt: number, verify: boolean) => Promise<ShellProbeResult>;
onEvidence?: (evidence: RetryEvidence) => Promise<void> | void;
}): Promise<ShellProbeResult> {
const execution = await runBoundedRetry({
operation: "inference.switch.verify",
owner: "inference-provider",
idempotence: "idempotent",
maxAttempts: options.attempts,
run: (attempt) => options.run(attempt, true),
classify: (result) => {
if (result?.exitCode === 0) return { outcome: "passed" };
return {
outcome: "failed",
failureClass:
result && isTransientInferenceSetFailure(result) ? "transient-external" : "deterministic",
};
},
delayMs: (attempt) => attempt * 5_000,
sleep: options.delay,
onEvidence: options.onEvidence,
});
return execution.value!;
}