Skip to content

[feat]: capture FX graph metadata during optimization profiles - #19

Open
aryan5v wants to merge 10 commits into
universal-ws1-generation-launcherfrom
fx-capture-hooks
Open

[feat]: capture FX graph metadata during optimization profiles#19
aryan5v wants to merge 10 commits into
universal-ws1-generation-launcherfrom
fx-capture-hooks

Conversation

@aryan5v

@aryan5v aryan5v commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Stacked on #18 (universal-ws1-generation-launcher), which adds the worker-side optimization profile this builds on. Fork-only — not for upstream.

What this does

Adds a generic FX-capture hook to the existing dedicated profiling generation. Repeated transformer/module calls are captured with no Wan- or LTX-specific code anywhere in the path.

fastvideo/optimization/fx_capture.py (new, torch-only):

  • default_capture_targets() selects any nn.ModuleList with >=2 identically-typed children — the model-independent signature of a block stack. Every member is hooked so call/shape frequency covers the whole stack; one trace is taken per stack.
  • Forward hooks stay cheap: input/output tensor signatures (positional and keyword) plus counters. The symbolic_trace runs in finalize(), after the profiler window closes, so captured graphs never contaminate the exported timings.
  • Exports operations, dependency edges, tensor signatures, safe scalar constants, calls / shape_frequency, graph breaks and unsupported ops, with a stable content fingerprint the consumer can recompute as an integrity check.

Export schema

The profile JSON gains optional, additive keys — capture, regions, graph_breaks, unsupported — only when capture ran. capture.capture_schema_version is versioned independently of the export's schema_version, so a capture-format change does not invalidate timing-only readers, and older exports (rows only) still parse unchanged.

Off by default

Capture requires both FASTVIDEO_OPTIMIZATION_PROFILE_OUTPUT (existing) and FASTVIDEO_OPTIMIZATION_PROFILE_CAPTURE_FX=1 (new, default off). Clean timing runs and normal generation attach no hooks at all. New knobs: ..._FX_TRACER, ..._FX_MAX_SCOPES (64), ..._FX_MAX_SHAPES (8) to bound the export.

Failure handling

Target selection, hooking, per-call observation, tracing and finalize are each guarded. Failures are recorded as graph_breaks entries or capture.errors and the run continues — the timing export is still written.

Privacy

assert_metadata_only() walks the finished payload before it is written and raises on any forbidden key (prompt, weights, values, activations, ...) or live tensor. Parameter/buffer get_attr reads are recorded as a note, never a value.

Testing

pytest fastvideo/tests/optimization/ -q -> 13 passed. CPU-only, no GPU needed; a fake pipeline drives a synthetic block stack through the hook.

Covered: region/frequency contents, keyword tensor inputs, capture off when not requested, capture off when no profile output, hook removal plus bit-identical outputs vs. an unhooked run, untraceable block recorded as fx_trace_failed with rows still exported, forced finalize exception recorded in capture.errors, shape-variant bounding, no forbidden keys in the written file, and fingerprint stability.

Consumer side round-trips: an export produced by this branch loads through the downstream discovery loader, passing its independent fingerprint recomputation and its pure-tensor safety check.

Note

The mypy pre-commit hook fails with FastVideo-main is not a valid Python package name — pre-existing, reproduces on untouched files (checkout directory name), unrelated to this change. yapf, ruff and codespell pass on all touched files.

Greptile Summary

This PR adds a generic FX graph metadata capture hook layered on top of the existing optimization profiler. It introduces FXCaptureSession, which hooks repeated nn.ModuleList block stacks, records tensor shape/dtype signatures during the profiling window, then runs FX traces (symbolicexportdynamo fallback) after the window closes to avoid contaminating profiler timings.

  • fastvideo/optimization/fx_capture.py (new, 958 lines): self-contained session class, privacy guard (assert_metadata_only), stable content fingerprinting, and bounded export with graceful failure recording throughout.
  • fastvideo/optimization/profiler.py: optimization_profile() now accepts an optional modules mapping; FX capture is attached before profiling and finalized (including hook removal) in a finally block; profiler rows emitted by the FX range hooks gain parent_module and scope_kind fields.
  • fastvideo/tests/optimization/test_fx_capture.py (new, 565 lines): 13 CPU-only tests covering region/frequency data, hook cleanup, untraceable blocks, forced finalize failure, shape-variant bounding, privacy guards, and fingerprint stability.

Confidence Score: 5/5

Safe to merge; the two findings are edge-case quality issues in best-effort telemetry that degrade gracefully without affecting generation or timing output.

The core generation path is fully protected — capture is opt-in, all hook attachment and finalization is guarded with broad try/except, and failures are recorded as data rather than raised. The two observations are: (1) assert_metadata_only would produce a false-positive finalize_failed error if a model's nn.ModuleList happens to be named exactly a forbidden key (e.g. 'data'), silently dropping capture metadata while leaving timing intact; (2) the 'motionkernel::' prefix string is duplicated between fx_capture.py and profiler.py rather than imported from a single source. Neither affects production inference, timing correctness, or the written profile file in the common case.

Files Needing Attention: fastvideo/optimization/fx_capture.py — the assert_metadata_only call at line 984 and the hardcoded prefix string at line 652 are the two spots worth a second look before any future refactor.

Important Files Changed

Filename Overview
fastvideo/optimization/fx_capture.py New 958-line FX capture session with multi-mode tracing fallback, privacy guard, and bounded export; two P2 issues found: false-positive FORBIDDEN_KEYS check on scope_calls dict keys, and duplicated "motionkernel::" prefix string.
fastvideo/optimization/profiler.py Extends optimization_profile() to accept modules, wires FX capture lifecycle (attach before profiling, finalize in finally), and enriches profiler rows for motionkernel:: events with parent_module/scope_kind.
fastvideo/envs.py Adds four new env vars for FX capture control (CAPTURE_FX bool, FX_TRACER str, FX_MAX_SCOPES int, FX_MAX_SHAPES int); all default to off/conservative values and follow existing patterns.
fastvideo/pipelines/composed_pipeline_base.py Single-line change: passes self.modules to optimization_profile() via getattr with None fallback; correct and safe.
fastvideo/tests/optimization/test_fx_capture.py 565-line test suite covering 13 CPU-only scenarios including hook cleanup, untraceable blocks, forced finalize failure, shape-variant bounding, privacy guards, and fingerprint stability; well-structured with synthetic modules.
tests/local_tests/optimizations/test_optimization_profiler.py Adds a single test verifying that motionkernel:: prefixed profiler events are correctly renamed and annotated with parent_module and scope_kind in _rows().

Reviews (3): Last reviewed commit: "Merge pull request #20 from aryan5v/agen..." | Re-trigger Greptile

Add a model-independent FX capture hook that runs alongside the existing
worker-side optimization profile. Repeated module stacks are found
structurally (any nn.ModuleList whose children share one class), so no
architecture is named anywhere in the capture path.

- forward hooks record only tensor signatures and call counters
- symbolic tracing runs after the profiler window closes, so captured
  graphs never contaminate the exported timings
- export gains optional regions / graph_breaks / unsupported keys plus a
  separately versioned capture block; readers that only understand rows
  are unaffected
- capture stays off unless the optimization profile is requested and
  FASTVIDEO_OPTIMIZATION_PROFILE_CAPTURE_FX is set
- trace and finalize failures are recorded as data; generation and the
  timing export continue
- payloads are asserted metadata-only: no tensor values, weights, or
  prompts
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 563c7c29-57f8-4947-83eb-d703773ce029

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread fastvideo/optimization/profiler.py
Comment thread fastvideo/optimization/profiler.py
Comment thread fastvideo/optimization/fx_capture.py Outdated
Comment on lines +429 to +431
if graph is None:
raise RuntimeError("trace result has no FX graph")
operations, dependencies, constants, notes = _extract_graph(graph)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 no_tensor_inputs graph-break appended on every call, not just the first

Each invocation of _observe that finds no tensor inputs unconditionally appends a new entry to self._graph_breaks. For a model that calls a non-tensor-input scope N times during the profiling window, N entries accumulate before _coalesce reduces them at finalize(). While _coalesce handles this correctly in the final output, the in-flight list is unbounded. A small per-scope dedup set would prevent repeated appends while keeping the semantics identical.

Prompt To Fix With AI
This is a comment left during a code review.
Path: fastvideo/optimization/fx_capture.py
Line: 429-431

Comment:
**`no_tensor_inputs` graph-break appended on every call, not just the first**

Each invocation of `_observe` that finds no tensor inputs unconditionally appends a new entry to `self._graph_breaks`. For a model that calls a non-tensor-input scope N times during the profiling window, N entries accumulate before `_coalesce` reduces them at `finalize()`. While `_coalesce` handles this correctly in the final output, the in-flight list is unbounded. A small per-scope dedup set would prevent repeated appends while keeping the semantics identical.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code Fix in Cursor Fix in Conductor

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant