Skip to content

[python] Add RoboMIND AgileX HDF5 pipeline - #9445

Merged
JingsongLi merged 6 commits into
apache:masterfrom
YannByron:m0/pr2-robomind-pipeline
Aug 29, 2026
Merged

[python] Add RoboMIND AgileX HDF5 pipeline#9445
JingsongLi merged 6 commits into
apache:masterfrom
YannByron:m0/pr2-robomind-pipeline

Conversation

@YannByron

Copy link
Copy Markdown
Contributor

Summary

Add a RoboMIND AgileX sample that uses the same HDF5 transform contract for local and Ray ingestion. The pipeline writes episode and frame tables, materializes canonical actions, and refreshes train-split statistics independently.

Changes

  • Add pypaimon.ray.load_from_hdf5 with shared HDF5 discovery, transform validation, and a single coordinated Paimon commit.
  • Add separate RoboMIND AgileX episode and frame transforms for local and Ray ingestion.
  • Keep deletion vectors and Vortex vector storage enabled while storing image and depth payloads as raw bytes.
  • Split canonical action materialization from statistics refresh and process frame updates by Paimon split.
  • Generate multi-episode AgileX HDF5 fixtures in pytest, with an optional flag for downloaded customer data.
  • Document the dataset layout, split semantics, table options, local usage, and Ray usage.

Testing

  • python -m pytest -q pypaimon/tests/multimodal_hdf5_test.py pypaimon/tests/ray_hdf5_test.py pypaimon/tests/robomind_agilex_pipeline_test.py (29 passed, 1 skipped)
  • Ruff on all changed Python files
  • git diff --check origin/master...HEAD
  • Customer dataset test with --robomind-agilex-input (requires a downloaded RoboMIND dataset)

Notes

This builds on the HDF5 DataSource ingestion merged in #9411. Repeating ingestion appends duplicate rows by design; a future merge-into mode can provide episode-level upserts.

YannByron and others added 4 commits August 28, 2026 14:09
Reuse the HDF5 transform contract for local and Ray ingestion, then materialize canonical actions and independently refresh train statistics.

Co-Authored-By: Codex <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
AI-Model: gpt-5.6-sol
AI-Contributed/Feature: 1112/1112
AI-Contributed/UT: 342/342
Separate the module constants from the EpisodeSource class declaration so the Python CI matrix passes E302.

Co-Authored-By: Codex <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
AI-Model: gpt-5.6-sol
AI-Contributed/Feature: 2/2
AI-Contributed/UT: 0/0
Skip only the end-to-end ingestion tests when vortex-data is unavailable while retaining transform and statistics coverage on older Python versions.

Co-Authored-By: Codex <noreply@anthropic.com>
Co-Authored-By: Codex <noreply@openai.com>
AI-Model: gpt-5.6-sol
Co-Authored-By: Codex <noreply@ai-tool.com>
AI-Contributed/Feature: 0/0
AI-Contributed/UT: 9/9
Return exact Ray commit metadata without rescanning tables, stabilize action statistics, and strengthen validation and pipeline tests.

Co-Authored-By: Codex <noreply@anthropic.com>
AI-Model: gpt-5
Co-Authored-By: Codex <noreply@ai-tool.com>
AI-Contributed/Feature: 204/204
AI-Contributed/UT: 193/193

@JingsongLi JingsongLi 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.

Inline findings on the current PR head.

frame_count = lengths.pop()
if frame_count <= 0:
raise ValueError("%s: episode has no frames." % source_key)
_instruction(h5, source_key)

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.

[P1] Read language metadata from the published sidecar annotations

The published AgileX trajectory.hdf5 layout contains the master, puppet, and observations datasets, but not language_raw or language_distilbert; RoboMIND publishes task-language annotations separately in CSV/JSON. Following the documented --input .../h5_agilex_3rgb path therefore fails on the first file here. The synthetic fixture masks this by adding both datasets itself. Please load/derive the instruction from the official sidecar/path metadata and make the embedding external or optional, then cover the actual published HDF5 shape in a test. References: official AgileX HDF5 layout, RoboMIND dataset card.

@YannByron YannByron Aug 28, 2026

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.

Thanks. The current Hugging Face example_data we downloaded does contain both language_raw and language_distilbert, but I agree that neither field is part of the published AgileX HDF5 schema. Commit 09fc963 now treats them as optional: present datasets remain strictly shape-validated, while absent datasets produce null instruction metadata. I also added a test using the published HDF5 shape without either language dataset.

Comment thread paimon-python/pypaimon/sample/robomind_agilex.py
Comment thread paimon-python/pypaimon/sample/robomind_agilex.py
Comment thread paimon-python/pypaimon/sample/robomind_agilex.py Outdated
YannByron and others added 2 commits August 28, 2026 19:25
Keep the sample's recommended table contract stable when multimodal facade defaults change on the PR merge base.

Co-Authored-By: Codex <noreply@anthropic.com>
AI-Model: gpt-5
Co-Authored-By: Codex <noreply@ai-tool.com>
Co-Authored-By: Codex <noreply@openai.com>
AI-Contributed/Feature: 2/2
AI-Contributed/UT: 0/0
Treat unofficial language datasets as optional, reuse one row-id file index across canonical-action batches, and allow compatible backfill retries. Document the Vortex runtime requirements.

Co-Authored-By: Codex <noreply@anthropic.com>
AI-Model: gpt-5
Co-Authored-By: Codex <noreply@ai-tool.com>
Co-Authored-By: Codex <noreply@openai.com>
AI-Contributed/Feature: 123/123
AI-Contributed/UT: 124/124

@JingsongLi JingsongLi 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.

Re-reviewed the fixes on the latest head. The previous findings are addressed, but the new batched row-id update API introduces one correctness regression.

if updater is None:
updater = TableUpdateByRowId(
self.table, self.commit_user, commit_identifier)
updater.update_columns(table, cols)

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.

[P1] Coalesce updates that target the same file

Calling update_columns independently for each input batch is only safe when the batches map to disjoint first_row_id groups. If two batches update different rows in the same base file, each call rereads the same snapshot baseline and writes a full delta for the same write_cols and first_row_id; after both messages are committed, one batch's changes are lost. I reproduced this on the current head with a real batch table: batch 1 updates row 0 from age 25 to 26, batch 2 updates row 1 from 30 to 31, and the committed table reads [26, 30, 35, 40, 45] instead of [26, 31, 35, 40, 45]. The new mock-only test does not exercise file generation or reads, so it misses this. Please coalesce updates per target first_row_id and write one delta per target file (or explicitly reject batches whose target groups overlap), and add this same-file end-to-end case.

@YannByron YannByron Aug 29, 2026

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.

Thanks for reproducing this. In the current local RoboMIND backfill path, updates come directly from Paimon planned splits. DataEvolutionSplitGenerator groups files by first_row_id before packing splits, and _iter_raw materializes each complete split as one batch, so the same first_row_id group is not expected to cross batches in this specific flow.

However, you are right that the local batch API neither expresses nor enforces this precondition, so arbitrary batches can hit this correctness issue. I also overlooked the existing Ray implementations. The public Ray update_by_row_id API already determines first_row_id before writing, shuffles and groups updates by it, coalesces all updates for the same target file in one worker, and performs one coordinated commit. RoboMIND tables enable deletion vectors, so their same-table backfill should instead reuse the existing optimized Ray self-merge path, which reads live rows from a pinned snapshot and schedules one update task per target file group. Therefore, no separate distributed update engine is needed.

For this PR, I will add exact overlap detection for the affected first_row_ids in the local batch API and fail before commit, aborting staged files instead of silently losing updates. I will also add the same-file local end-to-end test and distributed multi-block same-file coverage, and wire the RoboMIND Ray backfill to the existing self-merge path.

@JingsongLi JingsongLi 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.

+1

@JingsongLi
JingsongLi merged commit d53103a into apache:master Aug 29, 2026
11 checks passed
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.

2 participants