Skip to content

feat(analysis): swim-lane viewer and aiperf analyze CLI#1116

Open
ajcasagrande wants to merge 1 commit into
mainfrom
ajc/swim-lane-viewer
Open

feat(analysis): swim-lane viewer and aiperf analyze CLI#1116
ajcasagrande wants to merge 1 commit into
mainfrom
ajc/swim-lane-viewer

Conversation

@ajcasagrande

@ajcasagrande ajcasagrande commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #1102 (ajc/metrics-accumulator-engine) — carve-out of the swim-lane viewer and the aiperf analyze CLI from the agentx branch.

Adds post-run visualization of completed AIPerf artifacts:

  • src/aiperf/analysis/swim_lane.py — session swim-lane renderer over profile_export.jsonl: one lane per concurrent session slot (greedy slot reuse), subagent sessions (agent_depth > 0) nested as child tiers below their root session's lane, plus concurrency / throughput / tokens-in-flight / TTFT-band curves built on the same aiperf.analysis.sweepline primitives the metrics accumulator uses (from feat(metrics): accumulator engine with records-pipeline wiring #1102).
  • src/aiperf/analysis/swim_lane_viewer.html — single-file interactive canvas trace viewer; the JSON payload is embedded gzip+base64 and inflated client-side via the native DecompressionStream (no vendored JS dependencies).
  • src/aiperf/cli_commands/analyze.py — new aiperf analyze dispatcher for post-run artifact analysis subcommands.
  • src/aiperf/cli_commands/swim_lane.pyaiperf analyze swim-lane <run_dir>... [-o OUT] [-c N] [--ramp S] [--html] writes swim_lane.png (and optionally swim_lane.html).
  • tests/unit/analysis/test_swim_lane.py — root-map resolution (authoritative root_correlation_id grouping + legacy parent_correlation_id walk), lane layout/nesting, PNG/HTML render, and v1/v2 ramp-config parsing.
  • docs/cli-options.md regenerated (make generate-cli-docs).
  • tools/ruff_baseline.json: two C901 (complexity) baseline entries for the matplotlib-heavy plot_swim_lane / write_swim_lane_html renderers, mirroring the agentx branch's baseline.

Deliberate overlap with the upcoming turn-messages PR

This PR owns src/aiperf/cli_commands/analyze.py and the analyze wiring line in src/aiperf/cli.py. A sibling agentx carve-out PR (turn-messages viewer) will add its app.command(... turn_messages ...) registration to the same analyze.py dispatcher — that reference was deliberately stripped here so aiperf analyze works standalone with only swim-lane registered. Expect a small, intentional merge overlap in those two files.

Adaptations from the agentx branch

  • analyze.py: dropped the turn-messages subcommand registration (sibling PR adds it back).
  • cli_commands/swim_lane.py: options made keyword-only (*, after run_dirs) to satisfy the ergonomics ratchet at the site instead of growing the baseline; behavior-neutral for cyclopts (all options carry explicit Parameter names, RUN-DIRS stays positional).
  • Base-compat verified: root_correlation_id and context_overflow_skip are not emitted by this branch's MetricRecordMetadata — both are read via .get() with graceful fallbacks (legacy parent-walk grouping; status 0), so exports from this branch render correctly today and pick up richer nesting when the agentx metadata lands.

Test + smoke evidence

  • uv run pytest -n auto tests/unit/analysis/166 passed (includes the 33 new swim-lane tests).
  • uv run pytest --collect-only -q tests/unit/ — 14304 collected, no collection errors.
  • make check-ruff-baselined — OK (121 total, 121 baselined, 0 new).
  • make check-ergonomics — OK (61 total, 61 baselined, 0 new).
  • Import smoke: aiperf.analysis.swim_lane, aiperf.cli_commands.{analyze,swim_lane}, aiperf.cli all import; viewer template resolves next to the module.
  • End-to-end CLI smoke: uv run aiperf analyze --help, uv run aiperf analyze swim-lane --help, then drove uv run aiperf analyze swim-lane /tmp/swimlane-smoke -c 2 --html against a synthetic run dir (main session + 2 parallel subagents + 1 plain session, v2 ramp config) — wrote swim_lane.png (correct nesting, ramp marker, target line) and swim_lane.html; decoded the embedded gzip payload and verified all 4 sessions, nSlots=2, rampS=2.0, subagent peaks.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a new analysis command to generate swim-lane visualizations for completed runs.
    • Supports exporting a PNG report and an optional interactive HTML viewer.
    • The viewer includes session lanes, concurrency trends, and run timing details.
  • Bug Fixes

    • Improved handling of nested and concurrent sessions so related activity is displayed more accurately.
    • Better resilience when some run data is missing or invalid.
  • Tests

    • Added coverage for session grouping, layout behavior, and output rendering.

@ajcasagrande ajcasagrande added the AgentX Feature for AgentX label Jul 7, 2026
@github-actions github-actions Bot added the feat label Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

Try out this PR

Quick install:

pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@798274bf58fdc1159cef82ac992cd6fa5de8aa89

Recommended with virtual environment (using uv):

uv venv --python 3.12 && source .venv/bin/activate
uv pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@798274bf58fdc1159cef82ac992cd6fa5de8aa89

Last updated for commit: 798274bBrowse code

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.81503% with 84 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/aiperf/analysis/swim_lane.py 86.50% 33 Missing and 33 partials ⚠️
src/aiperf/cli_commands/swim_lane.py 30.76% 18 Missing ⚠️

📢 Thoughts on this report? Let us know!

Base automatically changed from ajc/metrics-accumulator-engine to main July 8, 2026 18:42
Signed-off-by: Anthony Casagrande <acasagrande@nvidia.com>
@ajcasagrande
ajcasagrande force-pushed the ajc/swim-lane-viewer branch from dcf36b8 to 798274b Compare July 8, 2026 20:35
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds a new "aiperf analyze swim-lane" CLI command that renders per-session swim-lane visualizations from AIPerf run data. It includes a new analysis module for session grouping, layout, and curve computation, a self-contained interactive HTML viewer, CLI wiring, tests, and documentation updates.

Changes

Swim-Lane Analysis Feature

Layer / File(s) Summary
Core data loading and grouping
src/aiperf/analysis/swim_lane.py
Loads and filters profile_export.jsonl records, groups them into sessions, computes spans/merged intervals, and resolves subagent root lanes with legacy fallback.
Curve computation and PNG/HTML generation
src/aiperf/analysis/swim_lane.py
Computes concurrency/throughput/KV/TTFT curves, renders the two-panel PNG via plot_swim_lane, and builds the gzip+base64 payload via write_swim_lane_html.
Interactive HTML viewer
src/aiperf/analysis/swim_lane_viewer.html
Implements a self-contained page decoding the embedded payload and rendering synchronized canvas charts with hover, zoom, pan, pinning, detail panel, URL hash sync, and PNG export.
CLI command wiring
src/aiperf/cli.py, src/aiperf/cli_commands/analyze.py, src/aiperf/cli_commands/swim_lane.py
Registers analyze app in the CLI, adds the swim-lane subcommand, and implements the command entrypoint invoking the plot and HTML writer functions with error handling.
Tests, docs, and baseline
tests/unit/analysis/test_swim_lane.py, docs/cli-options.md, tools/ruff_baseline.json
Adds unit tests for root resolution, layout, and renderers; documents CLI options; and records ruff C901 baseline exceptions.

Estimated code review effort: 4 (Complex) | ~75 minutes

Poem

A rabbit hops through lanes of time,
Concurrency curves in perfect rhyme,
Subagents nest, PNGs bloom bright,
HTML views spring into light,
Hop, click, zoom — the swim lane's mine! 🐰📊

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main addition of a swim-lane viewer and the new aiperf analyze CLI grouping.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/aiperf/analysis/swim_lane.py (1)

1154-1158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Read the viewer template via safe_read_template_path.

VIEWER_TEMPLATE.read_text() is an inline filesystem template read. The repo standard is to route template reads through aiperf.common.path_safety.safe_read_template_path.

♻️ Suggested change
+    from aiperf.common.path_safety import safe_read_template_path
+
     html = (
-        VIEWER_TEMPLATE.read_text()
+        safe_read_template_path(VIEWER_TEMPLATE)
         .replace("__AIPERF_TITLE__", run_dir.name)
         .replace("__AIPERF_PAYLOAD_B64__", payload_b64)
     )

As per coding guidelines: "Read filesystem paths through aiperf.common.path_safety.safe_read_template_path instead of inline Path().read_text() or open().read()."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiperf/analysis/swim_lane.py` around lines 1154 - 1158, The viewer HTML
is reading the template directly with VIEWER_TEMPLATE.read_text(), which
bypasses the repo’s path-safety standard. Update the template load in
swim_lane.py to use aiperf.common.path_safety.safe_read_template_path for
VIEWER_TEMPLATE, then keep the existing __AIPERF_TITLE__ and
__AIPERF_PAYLOAD_B64__ replacements unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/aiperf/analysis/swim_lane_viewer.html`:
- Around line 1971-1985: The PNG export in exportPng is missing the TTFT latency
panel because the stacked canvas list excludes latCv. Update the list assembled
in exportPng to include the latency canvas in the correct position alongside
rulerCv, lanesCv, islCv, tputCv, kvCv, and navCv so the exported image matches
the full swim lane view.

---

Nitpick comments:
In `@src/aiperf/analysis/swim_lane.py`:
- Around line 1154-1158: The viewer HTML is reading the template directly with
VIEWER_TEMPLATE.read_text(), which bypasses the repo’s path-safety standard.
Update the template load in swim_lane.py to use
aiperf.common.path_safety.safe_read_template_path for VIEWER_TEMPLATE, then keep
the existing __AIPERF_TITLE__ and __AIPERF_PAYLOAD_B64__ replacements unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 08665681-6d07-4a1f-9e3e-72b46c719c50

📥 Commits

Reviewing files that changed from the base of the PR and between d41bd50 and 798274b.

📒 Files selected for processing (8)
  • docs/cli-options.md
  • src/aiperf/analysis/swim_lane.py
  • src/aiperf/analysis/swim_lane_viewer.html
  • src/aiperf/cli.py
  • src/aiperf/cli_commands/analyze.py
  • src/aiperf/cli_commands/swim_lane.py
  • tests/unit/analysis/test_swim_lane.py
  • tools/ruff_baseline.json

Comment on lines +1971 to +1985
function exportPng() {
const list = [rulerCv, lanesCv, islCv, tputCv, kvCv, navCv];
const out = document.createElement("canvas");
out.width = Math.max(...list.map((c) => c.width));
out.height = list.reduce((a, c) => a + c.height, 0);
const c = out.getContext("2d");
c.fillStyle = "#0a0d13";
c.fillRect(0, 0, out.width, out.height);
let y = 0;
for (const cv of list) { c.drawImage(cv, 0, y); y += cv.height; }
const a = document.createElement("a");
a.download = `${DATA.run}-swimlane.png`;
a.href = out.toDataURL("image/png");
a.click();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

PNG export omits the TTFT latency panel.

list stacks ruler, lanes, ISL, throughput, KV, and nav, but latCv is missing, so the exported PNG drops the latency band chart that is otherwise rendered in the UI. This looks unintentional given the comment "stack every panel canvas into one image."

🐛 Suggested fix
-  const list = [rulerCv, lanesCv, islCv, tputCv, kvCv, navCv];
+  const list = [rulerCv, lanesCv, islCv, tputCv, kvCv, latCv, navCv];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function exportPng() {
const list = [rulerCv, lanesCv, islCv, tputCv, kvCv, navCv];
const out = document.createElement("canvas");
out.width = Math.max(...list.map((c) => c.width));
out.height = list.reduce((a, c) => a + c.height, 0);
const c = out.getContext("2d");
c.fillStyle = "#0a0d13";
c.fillRect(0, 0, out.width, out.height);
let y = 0;
for (const cv of list) { c.drawImage(cv, 0, y); y += cv.height; }
const a = document.createElement("a");
a.download = `${DATA.run}-swimlane.png`;
a.href = out.toDataURL("image/png");
a.click();
}
function exportPng() {
const list = [rulerCv, lanesCv, islCv, tputCv, kvCv, latCv, navCv];
const out = document.createElement("canvas");
out.width = Math.max(...list.map((c) => c.width));
out.height = list.reduce((a, c) => a + c.height, 0);
const c = out.getContext("2d");
c.fillStyle = "`#0a0d13`";
c.fillRect(0, 0, out.width, out.height);
let y = 0;
for (const cv of list) { c.drawImage(cv, 0, y); y += cv.height; }
const a = document.createElement("a");
a.download = `${DATA.run}-swimlane.png`;
a.href = out.toDataURL("image/png");
a.click();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiperf/analysis/swim_lane_viewer.html` around lines 1971 - 1985, The PNG
export in exportPng is missing the TTFT latency panel because the stacked canvas
list excludes latCv. Update the list assembled in exportPng to include the
latency canvas in the correct position alongside rulerCv, lanesCv, islCv,
tputCv, kvCv, and navCv so the exported image matches the full swim lane view.

@ajcasagrande
ajcasagrande marked this pull request as ready for review July 8, 2026 20:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AgentX Feature for AgentX feat

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant