Skip to content

feat(experimental): add the offline replay trace contract and converter - #487

Open
0z5a wants to merge 1 commit into
Tencent-Hunyuan:mainfrom
0z5a:feat/aisim-replay-adapter
Open

0z5a wants to merge 1 commit into
Tencent-Hunyuan:mainfrom
0z5a:feat/aisim-replay-adapter

Conversation

@0z5a

@0z5a 0z5a commented Sep 18, 2026

Copy link
Copy Markdown

Incremental review: Files changed against main.

Summary

Adds the UniRL side of the optional AISimulate / Dynamo Replay integration proposed in #484:
a versioned trace contract and the workload-to-replay converters, with their CPU contract
tests. It is deliberately the half that can be built and verified without a simulator.

experimental/aisim/schema.py — the trace contract, stdlib only:

  • Three separated inputs: manifest (configuration and provenance), workload (what a
    predictor may read), measured outcomes (report-only). The separation is structural, not
    conventional: no converter function accepts a measured result.
  • Validation fails loudly on schema/version drift, non-finite or negative times, bool/float
    where an int belongs, missing lengths, unknown ids, self-referential or missing
    predecessors, dependency cycles and unsupported status.
  • A dependency arrival requires an explicit external_delay_ms (0.0 must be written
    deliberately), and a first-turn request carries an absolute release offset instead of a
    fabricated predecessor, so a missing wait can never be read as a zero wait.
  • failed / aborted / partial / retry / cancelled terminations raise instead of being
    dropped, and capture_status="incomplete" cannot produce a fidelity run.

experimental/aisim/convert.py — the converters:

  • to_open_loop preserves observed arrivals verbatim (rebased to a recorded origin) and adds
    no session_id, no wait_for and no synthesised tool wait; it also refuses to run under a
    dependency contract and refuses a window that mixes both arrival kinds.
  • to_dependency emits predecessor-plus-external-delay entries and keeps measured service time
    out of the predictor input; assert_no_outcome_leakage re-checks the converted rows.
  • Trajectory slots and collector barriers are not modelled. The sidecar says
    slot_semantics="not_modelled" and barrier_semantics="not_modelled" rather than inventing a
    representation that would make the replay look more faithful than it is.

Deliberately absent: runner.py and requirements.txt. The runner is the only file allowed to
import a simulator API, and it should land once a pinned simulator revision is confirmed instead
of being written against a guessed interface.

Related Issue

Refs #484. This is the adapter slice of that RFC; the RFC itself stays open for the interface
and profile questions it asks maintainers.

Test Plan

Environment: jk01, 8× L20 (driver 570.86.10), torch 2.11.0+cu128, base SHA
f8d95462837df00188dab1376e19347b43996307.

Lint and guards:

ruff check experimental/aisim/                     -> All checks passed!
ruff format --check experimental/aisim/            -> already formatted
python lint/check_docstring_lines.py               -> all one line
python lint/check_experimental_boundaries.py       -> ok (no core→experimental import, no sibling import)
python lint/check_core_dependencies.py             -> ok

CPU contract harness (repo-external, not committed), 18/18 not-FAIL — 16 PASS, 2 NOT_RUN:

python evidence/harness/test_rfc_p1_cpu.py --out evidence/validation_results_rfc_cpu.json
  T01 PASS  schema/version/unit/token range rejection, incl. NaN/inf and bool-as-number
  T02 PASS  unique ids; measured results matched exactly once; duplicate/missing/unknown rejected
  T03 PASS  fan-out rows stay one request each with a reverse id→group map
  T04 PASS  observed arrivals verbatim; no session/wait injected; contract mismatch rejected
  T05 PASS  time origin recorded and offsets rebased; non-ms units rejected
  T06 PASS  measured outcomes invisible to the converter; a leaked key is detected
  T07 PASS  full-coverage claim without a profile id, and missing provenance, rejected
  T08 PASS  importing and running the adapter loads no torch/ray/vllm/sglang
  T09 PASS  incomplete capture refused instead of reported as accurate
  T10 PASS  successor arrival rides predecessor completion plus the measured external delay
  T11 PASS  0.0 preserved; unknown wait and unanchored first turn both rejected
  T12 PASS  slot/barrier semantics labelled not_modelled rather than faked
  T13 PASS  equal-arrival tie-break is a stable function of the input sequence
  T14 PASS  retry/abort/partial lifecycles refused, so no window is renamed "complete"
  T15 PASS  fidelity claim without profile/topology provenance rejected
  T16 PASS  workload JSONL round-trips byte-identically and revalidates
  T17 NOT_RUN  request-level simulator output: no simulator is installed
  T18 NOT_RUN  external-runner failure handling: no runner process exists

Capability probe that scopes this PR (P0 of the RFC, recorded in the evidence directory):
import dynamo|aisimulate|spicaModuleNotFoundError for all three; no such package in
pip list; no AISimulate/Spica reference anywhere in the checkout; the two documented Dynamo
pages and the ai-dynamo PyPI name do resolve. Hence: contract and converters only, everything
simulator-facing NOT_RUN.

Compatibility / Risk

  • New package under experimental/; no existing module imports it, no core file is touched, and
    no dependency is added (stdlib only). Normal training is unchanged and the package can be
    deleted without a trace.
  • SCHEMA_VERSION (unirl.rollout.trace.v1) is stamped into every record and into the manifest;
    a field rename is a breaking change for the evidence artifacts, not for the runtime.
  • The exported workload is only as trustworthy as the capture hook that will produce it. That
    hook does not exist yet, which is why every capture-dependent claim is left NOT_RUN.
  • No timing model, cache model, topology model or K/V model is introduced here.

Reviewer Notes

  • Duplicate-work check (2026-09-18, 63 open PRs, keyword searches for AISimulate / replay /
    simulator): no PR claims a simulator integration, and "AISimulate" returns zero results. The
    closest replay work (feat(diffusion): batch video replay timesteps #394, perf(bagel): pack FlowGRPO replay steps #384, feat(flowgrpo): add bounded-memory chunked replay backward #465) is training-side batching and is unrelated.
  • Worth reading first: to_open_loop's refusal paths and assert_no_outcome_leakage — they are
    the two places that keep a predictor from being handed the answer.
  • Open questions deliberately left to the RFC: which capture hook to use, which pinned Replay
    interface is first, and whether the chosen runner can emit per-request completions at all. The
    adapter reports AGGREGATE_ONLY rather than reconstructing group tails from percentiles if it
    cannot.

Adds the UniRL side of the optional AISimulate/Dynamo Replay integration: a
versioned manifest/workload/measured-outcome contract (stdlib only) and the
workload-to-replay converter for the open-loop and dependency-arrival contracts.

Keeps measured outcomes unreachable from the converter, refuses unsupported
lifecycles and incomplete captures instead of trimming them, distinguishes a zero
wait from an unknown one, and labels trajectory-slot and collector-barrier
semantics as unmodelled rather than inventing a representation.

Refs Tencent-Hunyuan#483 (measurement tooling)
@github-actions github-actions Bot added the need review Ready and waiting for review label Sep 18, 2026
@zzhuoxin1508

Copy link
Copy Markdown
Collaborator

Same note as on #484: we currently only have H20. If the Replay / AI Configurator tables have no calibrated H20 profile, I don't think this adapter should land yet — the H20 data would need to come first, otherwise we cannot validate replay against real execution.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

need review Ready and waiting for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants