Skip to content

Replay & Causality API

Every symbol here is exported from the omniloop top-level package and appears in omniloop.__all__.

from omniloop import (
DeterministicReplay, InputReplayer, ReplayReport,
FunctionalTrainingLoop,
cause_of, effects_of, load_events,
find_divergence, fnv1a64, find_repo_root,
export_tuned_config_from_journal,
)

TrainingLoop.replay(journal_path, source, tunable=(), ...)

Section titled “TrainingLoop.replay(journal_path, source, tunable=(), ...)”

Re-executes a recorded run, sourcing mutations from the journal instead of live IPC and verifying a state hash per tick. Returns a TrainingLoop that behaves like the original, so the loop body needs no changes.

from omniloop import TrainingLoop
loop = TrainingLoop.replay("run.omni", cfg, tunable=["learning_rate"])
for _ in range(steps):
with loop.tick():
train_step(cfg)
report = loop.replay_report()
print(report.ticks_verified, report.first_divergence)

Determinism guarantee. Replay feeds back recorded inputs — mutations, IPC commands, and any seeds the loop recorded — and verifies the state hash at each tick that has one. It does not control sources of nondeterminism outside the journal: unseeded RNG, wall-clock reads, thread scheduling, GPU kernel non-associativity, or filesystem state. A divergence therefore means “this run did not reproduce”, not necessarily “OmniLoop replayed it wrong”; the first divergent tick is where to look.

What the hash covers. The state hash is taken over the telemetry frame with provenance removed: every __sys_* key (pid, script path, interpreter, argv, journal path), the captured __source_files__, and the volatile status fields (timestamp, thread_state, is_safety_tripped, error_message, watch_trip, __tick_duration_ns__). Those describe the invocation, not the computation, and necessarily differ between a recording and its replay — hashing them would make every replay diverge at its first checkpoint no matter what the loop computed. Everything else in the frame, including your metrics, tunables, and __exec_active__, is part of the hash.

The Rust-backed index underneath TrainingLoop.replay(). Pre-indexes inputs, hash checkpoints, and telemetry by tick for O(1) lookup, so replaying a long journal does not re-scan it per tick.

from omniloop import InputReplayer
replayer = InputReplayer("run.omni")
print(replayer.tick_count())
verification = replayer.verify_tick(42, current_state_hash)

verify_tick returns a TickVerification, one of:

Variant Meaning
Match The recorded and recomputed hashes agree
Diverged { expected, actual } They disagree — this is the divergence point
NoCheckpoint This tick carries no recorded hash; nothing to compare

A thin wrapper over InputReplayer for driving a replay without TrainingLoop — the raw-primitives path.

from omniloop import DeterministicReplay
replay = DeterministicReplay("run.omni", output_path="replayed.omni")

Pass output_path to record the replayed run as its own journal, which is what you then compare against the original with find_divergence.

Summary returned at the end of a replay.

Field Meaning
total_ticks Ticks present in the journal
ticks_verified Ticks that carried a hash and were checked
first_divergence Tick of the first mismatch, or None

ticks_verified is usually lower than total_ticks: hashes are checkpoints, not per-tick records.

Returns the first tick at which two journals disagree, or None when every shared checkpoint matches. Checkpoints present in only one journal are ignored, so two runs recorded at different hash cadences still compare cleanly.

from omniloop import find_divergence
tick = find_divergence("sim.omni", "real.omni")
if tick is None:
print("sim and real agree at every shared checkpoint")
else:
print(f"first divergence at tick {tick}")

The 64-bit FNV-1a hash used for state hashing. Exposed so external tooling can reproduce a state hash without linking the core. The canonical hash excludes volatile keys (timestamps) by construction, so it is stable across runs.

Every mutation batch is assigned one server-generated UUID v4 correlation id, threaded through the IPC ring, the registry, and into journal records. Both directions of the causal chain are queryable.

What caused the state at this tick — the mutation or control records whose correlation id leads here.

What this change led to — every record downstream of that correlation id.

from omniloop import cause_of, effects_of
for record in cause_of("run.omni", tick=880):
print(record["kind"], record["summary"])
for record in effects_of("run.omni", "6f1c…"):
print(record["tick"], record["summary"])

The dashboard’s Event Console exposes both as one click per event.

Returns the full typed event stream (mutations, control, lifecycle, input) from a journal, without a native rebuild. Use it when you want the records as plain Python rather than a causal query.

export_tuned_config_from_journal(journal_path, out_path=None)

Section titled “export_tuned_config_from_journal(journal_path, out_path=None)”

Reconstructs the final tuned values from a journal’s mutation records alone — the offline counterpart to loop.export_tuned_config(). Use it to recover a hand-tuned configuration from a run whose process is long gone.

from omniloop import export_tuned_config_from_journal
values = export_tuned_config_from_journal("run.omni", "tuned.yaml")

omniloop inspect run.omni prints the same values without writing code.

For loops whose state is an immutable pytree, where mutating a config object in place is not an option.

from omniloop import FunctionalTrainingLoop
loop = FunctionalTrainingLoop(params, tunable=["learning_rate"])
@jax.jit
def step(params, batch):
...
for batch in data:
params = loop.apply_mutations(params) # returns an updated pytree
params = step(params, batch)
loop.log(loss=float(loss))

apply_mutations returns a new structure with pending edits applied, safe to use inside a jit-compiled step.

Locates an OmniLoop repository checkout by walking up from start (or the current directory), honouring OMNILOOP_REPO_ROOT. Used by omniloop up to find the dashboard; exported because integrations sometimes need the same answer. Returns None when there is no checkout — a pip-only install is a normal case, not an error.