Skip to content

# feat(data): add episode analysis workflows to dataviewer - #1234

Merged
Ali Zaidi (akzaidi) merged 14 commits into
mainfrom
feat/episode-analysis
Sep 2, 2026
Merged

Ali Zaidi (akzaidi) merged 14 commits into
mainfrom
feat/episode-analysis

Conversation

@akzaidi

@akzaidi Ali Zaidi (akzaidi) commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

feat(data): add episode analysis workflows to dataviewer

PR Soundrack: Juicy J & Project Pat - Dem Goats

Description

Added an Episode Analyzer workflow that combines trajectory metrics, VLM outcomes, persisted analysis, filterable labels, and language instructions in the dataviewer. The backend now stores structured per-episode analysis and supports importing selected analysis fields as labels, while the standalone labeling CLI processes LeRobot datasets with Qwen3-VL.

The VLM judge request flow now returns completed judgments synchronously from a worker thread instead of returning 202 Accepted and requiring client polling. Dataset instruction lookup also supports LeRobot v3 task metadata.

Related issues: None

Type of Change

  • 🐛 Bug fix (non-breaking change fixing an issue)
  • ✨ New feature (non-breaking change adding functionality)
  • 💥 Breaking change (fix or feature causing existing functionality to change)
  • 📚 Documentation update
  • 🏗️ Infrastructure change (Terraform/IaC)
  • ♻️ Refactoring (no functional changes)

Component(s) Affected

  • infrastructure/terraform/prerequisites/ - Azure subscription setup
  • infrastructure/terraform/ - Terraform infrastructure
  • infrastructure/setup/ - OSMO control plane / Helm
  • workflows/ - Training and evaluation workflows
  • training/ - Training pipelines and scripts
  • docs/ - Documentation
  • data-management/viewer/ - Dataset analysis and annotation
  • evaluation/vlm_judge/ - VLM judge service contract

Testing Performed

  • Terraform plan reviewed (no unexpected changes)
  • Terraform apply tested in dev environment
  • Training scripts tested locally with Isaac Sim
  • OSMO workflow submitted successfully
  • Smoke tests passed (smoke_test_azure.py)
  • Dataviewer validation passed (npm run validate)
  • Frontend validation passed (npm run validate from data-management/viewer/frontend)

Documentation Impact

  • No documentation changes needed
  • Documentation updated in this PR
  • Documentation issue filed

Bug Fix Checklist

Complete this section for bug fix PRs. Skip for other contribution types.

  • Linked to issue being fixed
  • Regression test included, OR
  • Justification for no regression test:

Checklist

Changes

Analysis and labeling

  • Persisted structured episode analysis alongside dataset labels and exposed read, write, and label-import endpoints.
  • Added motion analysis with selectable log-scaled and radian-based smoothness normalization.
  • Added a Qwen3-VL labeling CLI with multi-view sampling, structured output parsing, and resumable JSONL results.

Dataviewer interface

  • Added the Episode Analyzer tab with motion metrics, VLM judging, analysis results, labels, and language instructions.
  • Added collapsible analysis sections and retained their open state across episode navigation.
  • Added label search and dataset-wide import controls for supported analysis fields.

Compatibility and tests

  • Added LeRobot v3 task metadata support for task strings stored in episode metadata and Parquet columns.
  • Changed VLM judge requests from asynchronous polling to synchronous completed responses.
  • Added backend and frontend behavior coverage, including a regression test for duplicate trajectory tooltip labels.

… panels to dataviewer

- add Episode Analyzer tab, motion metrics, and analysis records
- add per-episode language instructions in LeRobot v3 task format
- add foldable right-pane sections persisted across episodes
- import analysis fields as filterable labels; add VLM labeling CLI

✨ - Generated by Copilot
@akzaidi
Ali Zaidi (akzaidi) requested a review from a team as a code owner July 21, 2026 18:58
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 0298a1e.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@codecov-commenter

Codecov Comments Bot (codecov-commenter) commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.13120% with 51 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.72%. Comparing base (235d4c6) to head (0298a1e).

Files with missing lines Patch % Lines
.../viewer/backend/src/api/services/lerobot_loader.py 36.84% 9 Missing and 3 partials ⚠️
...components/episode-analyzer/MotionMetricsPanel.tsx 76.66% 1 Missing and 6 partials ⚠️
...gement/viewer/backend/src/api/routers/vlm_judge.py 64.28% 5 Missing ⚠️
...nd/src/components/annotation-panel/LabelFilter.tsx 54.54% 2 Missing and 3 partials ⚠️
...end/src/components/annotation-panel/LabelPanel.tsx 88.57% 0 Missing and 4 partials ⚠️
...nnotation-workspace/AnnotationWorkspaceContent.tsx 40.00% 3 Missing ⚠️
...omponents/episode-analyzer/EpisodeAnalysisCard.tsx 85.71% 1 Missing and 2 partials ⚠️
...management/viewer/frontend/src/hooks/use-labels.ts 90.90% 1 Missing and 2 partials ⚠️
...nagement/viewer/frontend/src/stores/label-store.ts 91.89% 0 Missing and 3 partials ⚠️
...anagement/viewer/backend/src/api/routers/labels.py 97.89% 0 Missing and 2 partials ⚠️
... and 2 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1234      +/-   ##
==========================================
- Coverage   88.09%   87.72%   -0.37%     
==========================================
  Files         273      275       +2     
  Lines       22585    22241     -344     
  Branches     3143     3147       +4     
==========================================
- Hits        19897    19512     -385     
- Misses       2020     2049      +29     
- Partials      668      680      +12     
Flag Coverage Δ *Carryforward flag
go 100.00% <ø> (ø)
pester 84.47% <ø> (-1.67%) ⬇️ Carriedforward from cb70a90
pytest-data-pipeline 100.00% <ø> (ø) Carriedforward from cb70a90
pytest-dataviewer 89.66% <87.41%> (+0.11%) ⬆️
pytest-dm-tools 100.00% <ø> (ø) Carriedforward from cb70a90
pytest-evaluation 95.40% <ø> (ø)
pytest-fuzz 4.01% <0.00%> (-0.06%) ⬇️
pytest-inference 100.00% <ø> (ø) Carriedforward from cb70a90
pytest-shared-ci 100.00% <ø> (ø) Carriedforward from cb70a90
pytest-training 92.95% <ø> (+0.29%) ⬆️ Carriedforward from cb70a90
vitest 85.66% <83.33%> (+0.12%) ⬆️
vitest-app 85.66% <83.33%> (+0.12%) ⬆️
vitest-components 85.66% <83.33%> (+0.12%) ⬆️
vitest-features 85.66% <83.33%> (+0.12%) ⬆️
vitest-lib 85.66% <83.33%> (+0.12%) ⬆️
vitest-state 85.66% <83.33%> (+0.12%) ⬆️

*This pull request uses carry forward flags. Click here to find out more.

Files with missing lines Coverage Δ
...ement/viewer/backend/src/api/routes/ai_analysis.py 94.24% <100.00%> (+0.12%) ⬆️
...er/backend/src/api/services/trajectory_analysis.py 95.68% <100.00%> (+0.68%) ⬆️
...-management/viewer/frontend/src/api/ai-analysis.ts 100.00% <ø> (ø)
...tests__/support/annotationWorkspaceTestSupport.tsx 89.47% <100.00%> (+0.16%) ⬆️
...ation-workspace/AnnotationWorkspaceAnalyzerTab.tsx 100.00% <100.00%> (ø)
...annotation-workspace/AnnotationWorkspaceTopBar.tsx 100.00% <ø> (ø)
...ion-workspace/AnnotationWorkspaceTrajectoryTab.tsx 75.00% <ø> (ø)
...ement/viewer/frontend/src/hooks/use-ai-analysis.ts 82.35% <ø> (ø)
...agement/viewer/frontend/src/hooks/use-vlm-judge.ts 63.63% <ø> (+3.63%) ⬆️
...a-management/viewer/frontend/src/lib/api-client.ts 95.27% <100.00%> (+3.05%) ⬆️
... and 12 more

... and 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Ali Zaidi (akzaidi) and others added 5 commits July 21, 2026 19:40
- correct spelling and formatting findings
- cover analysis label import behavior and no-op paths
- restore backend coverage above the required threshold

✅ - Generated by Copilot
- cover Azure label storage factory wiring
- verify nested dataset label path resolution

✅ - Generated by Copilot

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nice feature set — the Episode Analyzer flow, persisted per-episode analysis, and the switch to a synchronous worker-thread judge all read well, and the import-from-analysis endpoint is genuinely well-tested. Leaving this as a comment (non-blocking); the inline notes are suggestions plus a few known gaps.

Please add user-facing docs. The PR is marked "no documentation changes needed," but several of these flows are unclear for end users and there's nothing in docs/, the dataviewer README, or a skill covering them. A short user guide would go a long way:

  • The Qwen3-VL labeling script (vlm_label_dataset.py): document that it's a script, how to run it, what it emits (labels.jsonl / labels.csv), and — most importantly — how those results actually reach the viewer. Right now nothing bridges the script output to the meta/episode_labels.json the Episode Analyzer reads, so it's not obvious how a user closes that loop.
  • The Episode Analyzer tab end-to-end: importing analysis fields as labels, and what each motion metric (including the new log-scaled vs radian-based smoothness modes) means.

Missing tests (known — captured inline, not blocking). The lower-coverage areas are called out on the relevant lines: _compute_normalized_smoothness + smoothness_mode validation, the smoothness_mode route field, LabelPanel (lowest patch coverage at 41%), and useImportAnalysisLabels. Keeping these as inline notes so they're easy to pick up.

A couple of small non-blocking items are inline too: the "resumable JSONL" claim doesn't match open("w") truncation; setAllEpisodeLabels resets saved labels for all episodes on import; the import UI can't reach the backend's overwrite/prefix; and the frontend still carries dead 202-polling scaffolding after the judge went synchronous.

One pre-existing observation (not introduced here, worth a follow-up issue): BlobLabelStorage.load uses except (json.JSONDecodeError, Exception) and reaches into provider privates — it can silently return empty labels that a later save persists over real data.

Comment thread data-management/viewer/backend/scripts/vlm_label_dataset.py
Comment thread data-management/viewer/backend/scripts/vlm_label_dataset.py Outdated
Comment thread data-management/viewer/backend/src/api/routes/ai_analysis.py
Comment thread data-management/viewer/frontend/src/hooks/use-labels.ts Outdated
Comment thread data-management/viewer/frontend/src/components/annotation-panel/LabelPanel.tsx Outdated
Comment thread data-management/viewer/backend/src/api/routers/vlm_judge.py

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.

PR Soundtrack Approved 🔥

Comment thread data-management/viewer/backend/src/api/services/lerobot_loader.py
Comment thread data-management/viewer/backend/tests/scripts/test_vlm_label_dataset.py Outdated
C-Neisinger and others added 5 commits July 29, 2026 14:59
…resolution

- explicitly use IPv4 for backend health check
- resolve VLM_JUDGE_ENABLED and VLM_JUDGE_BACKEND from .env if not set
- improve DATA_DIR resolution logic from .env

🔧 - Generated by Copilot
- add resumable VLM labeling and persisted analysis output
- preserve label edits and expose safe import controls
- remove stale judge polling and expand behavioral coverage

🔧 - Generated by Copilot

@akzaidi Ali Zaidi (akzaidi) left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Review result: changes required before merge

The branch is current with main at 6582c578, and the post-sync validation workflow passes. The following functional issues remain:

Blocking

  1. Partial analysis updates erase existing fields. labels.py:505-516 replaces the complete record even though every field is optional. Merge explicitly supplied fields or require a complete PUT representation.
  2. Concurrent label and analysis writes can lose data. The endpoints use an unlocked load-modify-save cycle, while Blob storage writes with unconditional overwrite. Add an atomic storage mutation with per-dataset locking and Blob ETag retries.
  3. Saved per-episode judge instructions are ignored. vlm_judge.py:147-195 resolves only request or dataset metadata, breaking annotation-only batch judging and cache retrieval. Restore request → saved annotation → dataset precedence.
  4. Synchronous VLM inference can exceed the deployed request limit. vlm_judge.py:176-248 keeps the request open while inference may take minutes; Azure Container Apps ingress limits requests to 240 seconds. Return a durable asynchronous job and poll its status.
  5. Inference lacks single-flight deduplication and a GPU concurrency bound. Concurrent misses can duplicate model execution, exhaust GPU memory, and race on the same cache temporary path. Add keyed coordination, a bounded executor or semaphore, and unique atomic cache temporaries.
  6. Malformed model output is persisted as complete. vlm_label_dataset.py:128-205 accepts any JSON object, fills defaults, and lets --resume skip it forever. Validate a strict response schema and retain schema failures as retryable errors.

Follow-ups

  • Make optional .env lookups in start.sh safe under errexit and pipefail.
  • Add immutable model revision input and provenance to the labeling CLI.
  • Return a nonzero CLI exit code when episode errors remain.
  • Memoize trajectory-derived analysis inputs during playback.

Fresh CI has no failed or pending checks. CodeQL alert #392 is pre-existing on main from July 9 and is not attributable to this PR. GitHub does not permit the PR author account to submit a formal request-changes review, so these blockers are recorded as a comment review.

@akzaidi
Ali Zaidi (akzaidi) merged commit 547e3ca into main Sep 2, 2026
52 checks passed
@akzaidi
Ali Zaidi (akzaidi) deleted the feat/episode-analysis branch September 2, 2026 16:53
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.

7 participants