API Guide¶
lm-resiliency exposes one entry point for enabling GEMINI checkpointing and SCOUT failure localization.
This guide covers the public integration contract; component design and validation details are linked at the end.
Public API Stability¶
The package separates supported interfaces from low-level research components:
| Namespace | Contract |
|---|---|
lm_resiliency |
Stable training integration API |
lm_resiliency.manager_api |
Stable platform-neutral manager API |
lm_resiliency.integrations.<framework> |
Stable explicit framework entry points |
lm_resiliency.integrations.torchrun |
Stable torchrun worker-adapter API |
lm_resiliency.experimental |
Unstable low-level components |
The stable package-root exports are:
| Area | Exports |
|---|---|
| Activation and lifecycle | enable_resiliency, FrameworkName, ResiliencySession, ResiliencyHandle |
| Configuration | InMemoryCkptConfig, ReplayHarnessConfig |
| Dynamic and MoE replay | ReplayWorkload, GroupedExpertMaterializer, LeadingDimensionMaterializer |
| AllToAll replay | AllToAllReplayPolicy, AllToAllCapture, AllToAllTrafficMatrix, BalancedAndPermutationPolicy |
| Durable certification | DurableCheckpointConfig, CallbackDurableCheckpointAdapter |
| Fault reporting | SCOUTFaultReport, SCOUTFaultCallback, OrchestrationHooks, replay_fault_reports |
| Recovery handoff | RecoveryDecision, RecoveryDecisionCallback |
| Checkpoint tuning | estimate_chunk_size |
| Fault injection evaluation | enable_fault_injection, SCHEMA_VERSION, FaultCampaign, FaultIncident, IncidentTrigger, IncidentLifetime, IterationRange, ClockSpec, ClockType, ClockOrigin, FaultSpec, FaultTarget, FailureType, FaultSurface, FaultScope, FaultMagnitude, CorruptionOperation, SafetyClass, RetriggerPolicy, FaultExecutor, CallbackFaultExecutor, FaultExecutionRequest, FaultExecutionResult, UnsupportedFaultError, FaultInjectionSession, InjectionStatus, FaultInjectionRecord, LocalizationResult, FaultEvaluation, CampaignReport, CampaignJournal, CampaignStateStore, MemoryCampaignStateStore, JsonCampaignStateStore |
The stable manager API exports are:
| Area | Exports |
|---|---|
| Coordination | OrchestrationHooks, RestartDestinationResolver, RecoveryDecision, RecoveryDecisionCallback |
| Fault reports | SCOUTFaultReport, SCOUTFaultCallback, replay_fault_reports, dispatch_replay_faults |
| Checkpoint transfer | CheckpointTransfer, TransferMetadataStore, make_transfer |
| Hardware health | HealthConfig, HardwareHealthMonitor, HealthEvent, HealthReading, HealthSeverity, HealthSource, NvmlSource |
| Configuration drift | local_fingerprint, find_drift, format_drift |
The stable torchrun integration exports are:
| Area | Exports |
|---|---|
| Worker contract | TorchrunWorkerAdapter, TorchrunWorkerContext, TorchrunWorkerAdapterError, get_torchrun_worker_context |
| Built-in adapters | NativePyTorchAdapter, NativePyTorchDDPAdapter, TorchTitanWorkerAdapter, MegatronWorkerAdapter, DeepSpeedWorkerAdapter |
| Manager coordination | TorchrunRecoveryCoordinator, TorchrunRecoveryRequest, TorchrunInitialPlacement, TorchrunSuccessorPlacement |
| Launcher configuration | TorchrunLaunchConfig |
| Node identity | derive_torchrun_node_id |
| Rendezvous registration | create_rendezvous_handler, get_rendezvous_handler_creator |
Other module paths are internal unless this guide identifies them as supported.
Objects under lm_resiliency.experimental may change within the 0.x release series.
Enable Through Torchrun¶
The lm_resiliency rendezvous backend installs framework-import monitoring
before the user module starts, then selects the corresponding worker adapter as
the training stack is imported. The user training module does not import
lm_resiliency:
torchrun \
--nnodes=1:1 \
--nproc-per-node=1 \
--rdzv-backend=lm_resiliency \
--rdzv-endpoint=/tmp/lm-resiliency-rdzv \
--rdzv-id=my-run \
--rdzv-conf="store_type=file,\
lm_resiliency_restart_context_path=/tmp/lm-resiliency-context/context.json,\
lm_resiliency_worker_config=/absolute/path/worker.toml" \
--module your_training.module \
[application arguments...]
Set the context path with the
lm_resiliency_restart_context_path rendezvous option. The runtime creates the
parent with owner-only permissions while constructing the rendezvous handler.
If the directory already exists with different ownership or group/other access,
startup fails closed instead of changing administrator-provisioned permissions.
The rendezvous handler derives a stable node identity from /etc/machine-id
and commits the first min_nodes unique registrations as the initial training
group. Additional registered nodes remain parked as standbys. Test harnesses
and container deployments may point LM_RESILIENCY_MACHINE_ID_PATH at another
absolute file containing a valid machine ID; duplicate identities fail closed.
Built-in adapter selection is inferred from framework imports:
| Imported framework | Objects passed to the existing framework integration |
|---|---|
| Native PyTorch | One unambiguous root torch.nn.Module and optimizer |
| TorchTitan | The initialized torchtitan.train.Trainer |
| Megatron Core | The model chunks, optimizer, and scheduler returned by setup_model_and_optimizer() |
| DeepSpeed | The engine returned by deepspeed.initialize() |
PyTorch is treated as tentative because every higher-level framework imports it. Importing TorchTitan, Megatron Core, or DeepSpeed before attachment selects that more specific adapter. Importing multiple higher-level supported frameworks fails closed instead of selecting one arbitrarily. Distributed workers also agree on the inferred framework when the default process group initializes, before any built-in adapter enters attachment collectives. When the default backend is NCCL, this one-time agreement uses a temporary Gloo group so it does not depend on the application's CUDA device-binding order.
Worker adapters do not accept a parallelism strategy. They pass the same
framework objects to the existing enable_resiliency() API that explicit user
code would pass. The existing framework integration remains the single owner of
DDP, FSDP2/HSDP, TP, SP, CP, PP, EP, expert-TP, ZeRO, and framework-specific
process-group discovery.
The distributed PyTorch adapter attaches only after a DDP or FSDP construction
boundary that every participating rank must cross. At the first recognized
distributed forward, all ranks agree that each owns exactly one valid
model/optimizer pair before any rank starts LM Resiliency attachment
collectives. The optimizer must own all trainable parameters of that model and
no parameters from another model. Multiple optimizers, foreign parameters, or
a distributed forward without a recognized all-rank construction boundary fail
closed before attachment. Single-process jobs retain
root-module-forward discovery. Bottom-up FSDP2/HSDP wrapping is reduced to the
outermost sharded root, and optimizer subclasses defined after bootstrap are
instrumented when the class is created.
The other built-ins attach at their framework-owned initialization boundary and
likewise fail closed if the expected complete object bundle is unavailable.
On a replacement generation, built-ins accept GEMINI restart contexts and load
only the exact manager-selected step. The live checkpoint-topology digest must
match the manager plan before checkpoint state is applied. Missing, corrupt,
unverified, newer, older, or topology-incompatible state fails closed. A
durable-source restart requires a custom worker adapter that owns the
framework's durable loader.
Before the user module starts, bootstrap publishes the manager-selected resume
position as LM_RESILIENCY_TORCHRUN_CHECKPOINT_STEP (0 for generation zero).
Zero-import applications can use that value to restore deterministic sampler or
input position while the adapter independently verifies the recovered
framework state.
After manager-selected GEMINI recovery, the built-in DeepSpeed adapter rejects
a later engine.load_checkpoint() call because it could overwrite the selected
model and optimizer state. DeepSpeed applications that also restore
framework-owned client state must provide a custom worker adapter that
coordinates both recovery mechanisms.
For generation zero, a successful DeepSpeed framework load is allowed before
the first resiliency-managed training step and seeds the resiliency counter
from engine.global_steps.
Built-in adapters also close their resiliency handle at the framework's normal
distributed teardown boundary, before process groups are destroyed. This
completes asynchronous checkpoint and replay work before user code reports a
clean shutdown.
Worker policy is a strict versioned TOML file:
schema_version = 1
interval = 10
enable_checkpoint = true
enable_detection = true
[checkpoint]
disk_folder = "/shared/checkpoints"
disk_flush_interval = 100
verify_integrity = true
[replay]
rotate_layers = true
Unknown fields, missing or unsupported schema versions, and invalid values fail
closed. Assigned nodes must agree on the exact policy contents at rendezvous,
and each worker revalidates that digest before parsing the policy. Disabled
feature sections are still validated. replication_jump must
be valid for the deployed checkpoint group; it is not inferred from the
launcher node count. checkpoint.disk_flush_interval must be nonnegative; zero
disables periodic disk persistence. checkpoint.replication_jump must be -1
or positive, and checkpoint.replication_chunk_size must be positive. Supplying
lm_resiliency_worker_config enables automatic worker
instrumentation; omitting it leaves explicit enable_resiliency() integrations
unchanged. Explicit integrations can call get_torchrun_worker_context() to
obtain run identity, hashed node identity, worker width, generation, logical
slot, topology digest, and the manager-selected recovery decision without
duplicating them as application arguments. Workers obtain their local width from torchrun's standard
LOCAL_WORLD_SIZE environment and verify replacement plans against it.
Managers use TorchrunRecoveryCoordinator with the same c10d store used by the
rendezvous backend. It exposes committed initial placement, publishes immutable
same-node or replacement successor generations, and closes the run to wake
parked standbys. After successor publication, the coordinator renews a
generation-specific store lease until the manager deadline. Agents combine its
sequence and remaining duration with host-local monotonic time, so restart
eligibility does not depend on synchronized wall clocks. Keep the coordinator
alive through successor admission and poll check_health() while waiting.
Transient lease-store failures are retried; persistent renewal failure is
latched and raised by check_health() and subsequent coordinator operations.
TorchrunLaunchConfig constructs the common LM Resiliency torchrun arguments
without owning subprocess, scheduler, SSH, or GPU placement behavior.
Custom stacks set adapter = "package.module:factory" in the worker TOML. The
factory receives TorchrunWorkerContext and returns an object implementing
install(context). A stack adapter owns framework-specific discovery and loop
state. Successor contexts include the manager-selected checkpoint source, step,
durable checkpoint ID when applicable, checkpoint manifest ID, topology digest,
and recovery deadline. Torchrun itself cannot infer scheduler, dataloader
position, or arbitrary caller-owned iteration state safely.
Enable Resiliency¶
Initialize torch.distributed, create the framework training objects, and call enable_resiliency once on every rank:
from lm_resiliency import enable_resiliency
resiliency = enable_resiliency(
model,
optimizer,
interval=10,
)
train()
Call enable_resiliency after framework initialization and before the training loop.
The returned handle owns hooks, background workers, and process groups created by the integration.
The integration releases these resources automatically at normal process exit.
interval is the shared optimizer-step cadence.
When GEMINI and SCOUT are both enabled, checkpoint capture and validation use the coordinated cadence described in GEMINI checkpointing.
Framework Support¶
The package infers the framework from the first training object:
| Framework | First argument | SCOUT parallelism |
|---|---|---|
| PyTorch | torch.nn.Module |
DDP, FSDP2, HSDP, TP, SP, CP, PP, EP, expert TP |
| TorchTitan | Initialized Trainer |
DP, FSDP2, HSDP, TP, SP, CP, PP, EP, expert TP |
| Megatron Core | Model chunks in a list or tuple | DP, TP, SP, CP, PP, virtual PP, EP, expert TP |
| DeepSpeed | DeepSpeed engine | DP, ZeRO 1-3, TP, PP, Ulysses SP, EP, expert TP |
SCOUT derives equivalent-peer groups from each framework's topology. Dense replay uses data-parallel or FSDP peers while preserving the model-parallel coordinates required by the replayed layer. Expert replay uses expert-data-parallel peers while preserving EP and expert-TP coordinates. Exact localization requires at least three equivalent peers.
Use the package-root API for every framework.
Pass framework="pytorch", "torchtitan", "megatron", or "deepspeed" only when a custom wrapper makes automatic dispatch ambiguous.
TorchTitan accepts its initialized trainer directly:
resiliency = enable_resiliency(trainer, interval=10)
trainer.train()
The adapter checkpoints and restores trainer, scheduler, dataloader, optimizer, and model state. It also coordinates TorchTitan's durable loader with an earlier GEMINI recovery.
Megatron accepts the model chunks, optimizer, and scheduler created before its production train() call:
from megatron.training import get_args
from lm_resiliency import enable_resiliency
def attach_resiliency(model_chunks, optimizer, scheduler):
resiliency = enable_resiliency(
model_chunks,
optimizer,
opt_param_scheduler=scheduler,
interval=10,
)
get_args().iteration = resiliency.step_count
return resiliency
Call this helper immediately after Megatron creates the three training objects, then continue into its existing train() call.
Capture and restore caller-owned sample or dataset state with extra_state_fn and load_extra_state_fn.
DeepSpeed owns its optimizer, so pass only the engine:
resiliency = enable_resiliency(engine, interval=10)
Unsupported arguments for the selected adapter raise TypeError.
Evaluate Fault Localization¶
The fault injection evaluation kit binds a declarative campaign to PyTorch, TorchTitan, Megatron Core, or DeepSpeed training objects:
from lm_resiliency import (
CorruptionOperation,
FailureType,
FaultCampaign,
FaultIncident,
FaultScope,
FaultSpec,
FaultSurface,
FaultTarget,
IncidentLifetime,
IncidentTrigger,
enable_fault_injection,
)
campaign = FaultCampaign(
name="output-sdc",
incidents=(
FaultIncident(
incident_id="hidden-sdc",
trigger=IncidentTrigger(at=(20,)),
lifetime=IncidentLifetime(matching_calls=1),
faults=(
FaultSpec(
fault_id="hidden-sign-flip",
type=FailureType.TENSOR_CORRUPTION,
target=FaultTarget(
rank=0,
module_path="layers.2",
surface=FaultSurface.OUTPUT,
),
parameters={
"operation": CorruptionOperation.SIGN_FLIP.value,
"scope": FaultScope.FULL.value,
},
),
),
),
),
)
faults = enable_fault_injection(model, optimizer, campaign=campaign)
The framework optimizer boundary advances the training_iteration clock
automatically; the training loop remains unchanged.
The injector records verified ground truth independently of SCOUT and GEMINI,
then faults.evaluate(...) compares neutral localization results with expected
ranks, resources, and components.
Custom CampaignStateStore implementations must provide atomic
compare_and_swap(expected, updated) in addition to load() and save(), so
overlapping old and replacement workers cannot both claim a restart-stable
occurrence.
See Fault injection evaluation for manifests, framework
targets, permanent and intermittent schedules, destructive executors, scoring,
and safety boundaries.
Native PyTorch¶
Call the package-root API after constructing the model and optimizer.
Replicated models and DistributedDataParallel use the standard PyTorch state-dict path.
FSDP2 and HSDP models created with torch.distributed.fsdp.fully_shard() use a local-shard path automatically:
from torch.distributed.device_mesh import init_device_mesh
from torch.distributed.fsdp import fully_shard
from lm_resiliency import enable_resiliency
mesh = init_device_mesh(
"cuda",
(dp_replicate, dp_shard),
mesh_dim_names=("dp_replicate", "dp_shard"),
)
for layer in model.layers:
fully_shard(layer, mesh=mesh)
fully_shard(model, mesh=mesh)
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)
resiliency = enable_resiliency(model, optimizer, interval=10)
Use a one-dimensional mesh for pure FSDP2.
The integration infers FSDP2 or HSDP from the parameter DeviceMesh, checkpoints local DTensor shards, and unshards only the sampled module during SCOUT replay.
For HSDP, SCOUT compares ranks at the same shard coordinate across dp_replicate.
Pure FSDP without natural replicas uses shard peers.
For a nonstandard mesh, pass an object with dp_replicate and dp_shard attributes as parallelism_info.
When an initialized TorchTitan Trainer is passed, the adapter reads its ParallelDims object automatically.
For lower-level TorchTitan model-and-optimizer integration, pass parallelism_info explicitly.
SCOUT creates independent Gloo and NCCL detection groups from the model topology.
To provide custom groups, pass both group and nccl_group; supplying only one raises ValueError.
Configure Protection¶
GEMINI and SCOUT are enabled by default. Disable either component when only one is required:
# GEMINI only.
resiliency = enable_resiliency(
model,
optimizer,
enable_detection=False,
)
# SCOUT only.
resiliency = enable_resiliency(
model,
optimizer,
enable_checkpoint=False,
)
Use component configuration objects for nondefault behavior:
from lm_resiliency import (
InMemoryCkptConfig,
ReplayHarnessConfig,
enable_resiliency,
)
resiliency = enable_resiliency(
model,
optimizer,
interval=10,
checkpoint=InMemoryCkptConfig(
replication_jump=8,
disk_folder="/local_nvme/gemini",
run_id="training-run-2026-08-15",
verify_integrity=True,
),
replay=ReplayHarnessConfig(
rotate_layers=True,
embedding_check_interval=10,
hidden_check_interval=10,
output_check_interval=20,
optimizer_check_interval=10,
straggler_confirmation_rounds=2,
hang_stall_threshold_s=30.0,
),
)
The top-level interval overrides InMemoryCkptConfig.interval and the base ReplayHarnessConfig.check_interval.
Explicit embedding, hidden, output, and optimizer check intervals continue to override that base replay cadence.
See GEMINI configuration and SCOUT configuration for configuration guidance.
Instrument a DataLoader¶
SCOUT can measure sampled DataLoader.next() stalls from its out-of-band monitor:
train_dataloader = resiliency.instrument_dataloader(
train_dataloader,
name="train",
)
Instrument the DataLoader before creating its long-lived iterator. The original DataLoader is returned unchanged when SCOUT is disabled.
Instrument Checkpoint I/O¶
Wrap framework-owned checkpoint calls so the out-of-band observer can localize blocked or slow reads and writes:
with resiliency.checkpoint_io("write", name="periodic"):
save_checkpoint()
with resiliency.checkpoint_io("read", name="recovery"):
load_checkpoint()
SCOUT-certified durable checkpoint callbacks and fallback loads are instrumented automatically. Use the context manager for other framework checkpoint paths.
Add Caller-Owned State¶
The PyTorch adapter can include scheduler, sampler, or other caller-owned state:
def capture_extra_state():
return {
"scheduler": scheduler.state_dict(),
"sampler": sampler.state_dict(),
}
def restore_extra_state(state):
scheduler.load_state_dict(state["scheduler"])
sampler.load_state_dict(state["sampler"])
resiliency = enable_resiliency(
model,
optimizer,
extra_state_fn=capture_extra_state,
load_extra_state_fn=restore_extra_state,
)
Do not include model or optimizer state. The framework adapter captures those objects.
Configure Recovery¶
Provide the framework's durable loader as the fallback:
resiliency = enable_resiliency(
model,
optimizer,
load_fallback=load_durable_checkpoint,
)
GEMINI first attempts its available memory and node-local tiers.
It calls load_fallback only when no recoverable GEMINI checkpoint exists.
Use resiliency.step_count when the training loop needs an explicit resume point:
for step in range(resiliency.step_count, max_steps):
train_step()
Configure MoE Replay¶
Bind a qualified execution-regime catalog to the post-dispatch expert stage:
from lm_resiliency import (
GroupedExpertMaterializer,
ReplayHarnessConfig,
ReplayWorkload,
enable_resiliency,
)
workload = ReplayWorkload.from_moe_catalog(
catalog,
replay_modules=[model.post_dispatch_expert_stage],
materializer=GroupedExpertMaterializer(),
)
resiliency = enable_resiliency(
model,
optimizer,
replay=ReplayHarnessConfig(
workload=workload,
capture_inputs_by_value=True,
rotate_layers=False,
),
)
GroupedExpertMaterializer supports the common (tokens, tokens_per_expert, probabilities) contract.
For scalar recipe n_exec, it sets every local expert count to n_exec and resizes token-aligned inputs to n_exec * local_expert_count rows.
Use counts_input for a different count argument and alignment for backend row alignment.
Use LeadingDimensionMaterializer only when the expert boundary has no separate count or offset metadata.
ReplayWorkload.from_moe_catalog() marks the workload as expert replay, so Megatron, DeepSpeed, and mesh-based adapters select expert-data-parallel peers automatically.
For a custom expert workload built with ReplayWorkload.from_shapes(), pass peer_role="expert".
See MoE execution regimes for catalog qualification and exact fallback.
Configure AllToAll Replay¶
SCOUT captures Python-visible AllToAll tensor layouts, split matrices, sequence positions, and process-group membership. At each replay check, the configured policy generates bounded representative traffic matrices. The shared executor materializes deterministic rank-tagged payloads, executes the matrices on the captured process group, verifies routing exactly, and reports timing through C3 and Cross-PG localization.
The default BalancedAndPermutationPolicy executes at most two matrices:
- a balanced matrix for concurrent peer traffic; and
- a cyclic permutation matrix that places each sender's payload on one route while keeping per-rank send and receive volume equal.
The default payload budget is 4 MiB per rank and one tensor row is the minimum allocation unit. The matrices depend on process-group size, tensor row width, and sequence position rather than observed token routing, so equivalent data-parallel replicas remain comparable. This is representative coverage, not exhaustive coverage of every possible routing matrix.
Change the payload bound through ReplayHarnessConfig:
from lm_resiliency import (
BalancedAndPermutationPolicy,
ReplayHarnessConfig,
enable_resiliency,
)
resiliency = enable_resiliency(
model,
optimizer,
replay=ReplayHarnessConfig(
all_to_all_policy=BalancedAndPermutationPolicy(
max_payload_bytes_per_rank=2 * 1024 * 1024,
),
),
)
To define another policy, implement AllToAllReplayPolicy.generate(capture) and return one or more square AllToAllTrafficMatrix values.
capture.observed_splits exposes the reconstructed training matrix for trace-based policies.
Return matrices in deterministic order on equivalent replicas.
Set all_to_all_policy=None to disable this replay surface.
Report Faults¶
Direct integrations have two callback paths:
| Callback | Payload | Signals |
|---|---|---|
fault_callback |
ReplayResult |
SDC and replay-localized stragglers |
oob_fault_callback |
SCOUTFaultReport |
Hangs, DataLoader stalls, and checkpoint I/O stalls |
replay_fault_reports(result) converts a ReplayResult into JSON-ready SCOUTFaultReport records.
For confirmed communication stragglers, SCOUT automatically intersects slow process groups and reports the shared rank's host as scope="node".
The report includes the endpoint rank and host, supporting and contradictory group evidence, and confidence.
Use OrchestrationHooks when an external manager needs one normalized callback.
Certify Durable Checkpoints¶
DurableCheckpointConfig connects SCOUT evidence to framework-owned checkpoint bytes:
from lm_resiliency import (
CallbackDurableCheckpointAdapter,
DurableCheckpointConfig,
enable_resiliency,
)
adapter = CallbackDurableCheckpointAdapter(
save_candidate_fn=save_candidate,
load_checkpoint_fn=load_checkpoint,
commit_candidate_fn=commit_candidate,
quarantine_candidate_fn=quarantine_candidate,
)
resiliency = enable_resiliency(
model,
optimizer,
durable_checkpoint=DurableCheckpointConfig(
manifest_dir="/shared/checkpoints/scout",
environment_id=job_environment_id,
adapter=adapter,
),
)
Certification requires SCOUT.
An accepted dense check certifies its checkpoint immediately because the dense shape catalog has one entry.
Dynamic MoE training runs one configured replay-shape recipe per scheduled check.
After the first complete MoE catalog cycle, the framework persists the boundary as CANDIDATE.
The following complete clean cycle promotes that checkpoint to RECOVERY_VERIFIED and creates the next candidate.
The manifest directory must have durability equivalent to the framework checkpoint store.
Detected SDC and inaccessible-machine recovery select only the
recovery-verified manifest and do not call an unconstrained load_fallback.
See SCOUT checkpoint certification for the callback contract.
Select Recovery Trust¶
The launcher can classify a failure before recovery:
mode = resiliency.prepare_recovery(
"hang",
all_ranks_accessible=True,
)
The high-level choices are:
| Failure condition | Selected checkpoint |
|---|---|
| Accessible straggler | Latest complete GEMINI checkpoint |
| Hang or uncertain failure, all ranks accessible, full-catalog replay complete and clean | Latest complete GEMINI checkpoint |
| Full-catalog replay detects SDC, is incomplete, or returns missing evidence | RECOVERY_VERIFIED |
| SDC already detected | RECOVERY_VERIFIED |
| Required rank or machine inaccessible | RECOVERY_VERIFIED |
On relaunch, pass the selected mode when it is known externally:
from lm_resiliency import RecoveryMode, enable_resiliency
resiliency = enable_resiliency(
model,
optimizer,
recovery_mode=RecoveryMode.RECOVERY_VERIFIED,
)
An SDC decision is also persisted beside GEMINI checkpoint metadata so a same-storage restart cannot silently choose a newer candidate.
Manager Integration¶
OrchestrationHooks provides a platform-neutral boundary for fault reports, checkpoint selection, and restart destinations:
from lm_resiliency import enable_resiliency
from lm_resiliency.manager_api import OrchestrationHooks
hooks = OrchestrationHooks(
report_fault=lambda report: control.report_fault(rank, report),
report_recovery=lambda decision: control.report_recovery(rank, decision),
restart_destination=control.restart_destination,
)
resiliency = enable_resiliency(
model,
optimizer,
orchestration=hooks,
)
report_recovery receives a JSON-ready RecoveryDecision before the corresponding automatic fault report.
The decision identifies the failure class, selected trust mode, checkpoint
source, step, optional durable checkpoint ID, checkpoint-topology digest,
availability, and selection reason.
Checkpoint lookup in the reporting process is rank-local and noncollective because a fault may have already made the training process group unusable.
The manager can conservatively combine worker decisions, include the selected decision in its restart command, and pass recovery_mode to the relaunched integration.
The restarted GEMINI loader retains responsibility for collectively validating
and loading that exact manager-selected generation.
In-process SDC automatically emits a recovery-verified decision, while a replay-localized straggler emits a latest-GEMINI decision.
An OOB hang emits a conservative recovery-verified decision without entering a blocked training process or invoking replay.
An instrumented DataLoader stall emits a latest-GEMINI decision because the reporting worker remains accessible.
An instrumented checkpoint I/O stall also emits a latest-GEMINI decision.
Explicit calls to prepare_recovery(...) emit the resulting decision and retain it as last_recovery_decision.
Before replacing or migrating a worker, preserve its latest recoverable state:
step = resiliency.flush_for_restart()
destination = control.restart_destination()
if step >= 0 and destination is not None:
resiliency.copy_checkpoint_to(destination)
copy_checkpoint_to copies flushed local and peer shards.
Import make_transfer from lm_resiliency.manager_api when endpoint-addressed manager transfer is required.
The "torch_dist" and "nixl" backends share the CheckpointTransfer contract.
Every transfer is keyed, bounded by timeout_s, and validates endpoint, tensor layout, byte count, and per-chunk checksums before applying received buffers.
TorchDistTransfer uses a dedicated Gloo pair group even when the training world uses NCCL; both endpoints create the same pair in coordinated order, or the manager may pass an already-created dedicated Gloo process_group.
NIXL is one-sided while the torch-distributed backend requires both endpoints to participate, so make_transfer("nixl", ...) fails if NIXL is unavailable unless the manager explicitly sets allow_backend_fallback=True after arranging the two-sided protocol.
Launcher retry, placement, replacement, and quarantine policy remain outside this repository.
Monitor Hardware Health¶
The optional hardware-health API complements replay and out-of-band consensus with direct telemetry for permanent or imminent device failures.
It is not enabled automatically by enable_resiliency().
from lm_resiliency.manager_api import (
HardwareHealthMonitor,
HealthConfig,
NvmlSource,
)
monitor = HardwareHealthMonitor(
HealthConfig(poll_interval_s=5.0),
[NvmlSource(device_index=physical_gpu_index)],
on_event=manager_health_callback,
)
monitor.start()
The caller resolves physical_gpu_index and supplies manager_health_callback.
on_event receives one deduplicated HealthEvent for each fatal device-and-metric pair.
The built-in NVML source covers uncorrectable ECC, row-remap failure, NVLink error growth, temperature near shutdown, and device loss.
Custom HealthSource implementations can supply XID, PCIe, InfiniBand, NIC, HCA, or fabric telemetry.
Warnings such as correctable ECC growth, pending row remaps, nonfatal XIDs, and elevated temperature are logged but do not invoke on_event.
Call monitor.close() when the manager or worker lifecycle ends.
See SCOUT hardware telemetry for thresholds, localization granularity, and manager ownership.
Lifecycle¶
Every framework adapter returns the same lifecycle surface:
| API | Behavior |
|---|---|
step_count |
Completed optimizer steps, including the recovered resume point |
instrument_dataloader(...) |
Adds sampled DataLoader-stall instrumentation |
checkpoint_io(...) |
Marks a framework checkpoint read or write for OOB localization |
flush_for_restart() |
Flushes recoverable GEMINI state and returns its step, or -1 |
set_restart_destination(...) |
Sets an optional restart-mirror resolver |
copy_checkpoint_to(...) |
Copies flushed checkpoint shards |
prepare_recovery(...) |
Runs any required emergency recipe cycle and selects latest or recovery-verified trust |
last_recovery_decision |
Most recent JSON-ready checkpoint decision emitted to orchestration |
close() |
Optionally releases resources before process exit |
Normal training scripts do not need to call close().
Call it only when a long-lived process must tear down one training session before starting another.
Advanced Integration¶
Framework-specific entry points remain available for explicit devices, process groups, and nonstandard topology metadata:
lm_resiliency.integrations.pytorch.enable_resiliencylm_resiliency.integrations.torchtitan.enable_resiliencylm_resiliency.integrations.megatron.enable_resiliencylm_resiliency.integrations.deepspeed.enable_resiliency
Prefer the package-root API unless the automatic adapter cannot represent the topology.
Import low-level checkpoint managers, replay detectors, topology objects, and concrete transfer implementations from lm_resiliency.experimental only when accepting an unstable API.
Command-Line Interface¶
The package installs lm-resiliency-discover-moe-regimes for building a SCOUT replay catalog from saved profiler observations, an environment description, and a complete request manifest.
See MoE execution regimes for the command and catalog-qualification requirements.