robot-data-audit 0.7.2__tar.gz → 0.7.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. {robot_data_audit-0.7.2/robot_data_audit.egg-info → robot_data_audit-0.7.3}/PKG-INFO +8 -3
  2. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/README.md +7 -2
  3. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/pyproject.toml +1 -1
  4. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/formatter.py +6 -2
  5. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/temporal_metrics.py +36 -5
  6. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/components/common.py +616 -548
  7. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/i18n.py +96 -0
  8. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/3_Health_Overview.py +313 -266
  9. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/4_Episode_Explorer.py +1025 -844
  10. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3/robot_data_audit.egg-info}/PKG-INFO +8 -3
  11. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/robot_data_audit.egg-info/SOURCES.txt +2 -0
  12. robot_data_audit-0.7.3/tests/test_req3b_fps_floor.py +86 -0
  13. robot_data_audit-0.7.3/tests/test_ui_visual_audit.py +233 -0
  14. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/LICENSE +0 -0
  15. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/__init__.py +0 -0
  16. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/audit/__init__.py +0 -0
  17. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/audit/dataset_audit.py +0 -0
  18. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/audit/episode_audit.py +0 -0
  19. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/audit/rules.py +0 -0
  20. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/calibration/__init__.py +0 -0
  21. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/calibration/normalization.py +0 -0
  22. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/calibration/portable.py +0 -0
  23. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/calibration/reference.py +0 -0
  24. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/calibration/scorer.py +0 -0
  25. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/cli/__init__.py +0 -0
  26. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/cli/main.py +0 -0
  27. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/export/__init__.py +0 -0
  28. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/export/clean_export.py +0 -0
  29. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/io/__init__.py +0 -0
  30. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/io/lerobot_loader.py +0 -0
  31. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/io/schema.py +0 -0
  32. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/__init__.py +0 -0
  33. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/base.py +0 -0
  34. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/distribution.py +0 -0
  35. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/integrity.py +0 -0
  36. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/motion.py +0 -0
  37. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/temporal.py +0 -0
  38. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/video_integrity.py +0 -0
  39. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/visual_integrity.py +0 -0
  40. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/metrics/visual_quality.py +0 -0
  41. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/__init__.py +0 -0
  42. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/api_client.py +0 -0
  43. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/engine.py +0 -0
  44. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/local_fallback.py +0 -0
  45. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/preflight.py +0 -0
  46. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/recommend/types.py +0 -0
  47. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/__init__.py +0 -0
  48. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/aggregation.py +0 -0
  49. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/audit_history.py +0 -0
  50. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/json_report.py +0 -0
  51. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/summary.py +0 -0
  52. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/report/top_issues.py +0 -0
  53. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/__init__.py +0 -0
  54. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/app.py +0 -0
  55. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/components/__init__.py +0 -0
  56. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/1_Upload.py +0 -0
  57. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/2_Audit.py +0 -0
  58. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/5_Export.py +0 -0
  59. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/6_History.py +0 -0
  60. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/ui_app/pages/7_Recommend.py +0 -0
  61. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/rda/validation.py +0 -0
  62. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/robot_data_audit.egg-info/dependency_links.txt +0 -0
  63. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/robot_data_audit.egg-info/entry_points.txt +0 -0
  64. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/robot_data_audit.egg-info/requires.txt +0 -0
  65. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/robot_data_audit.egg-info/top_level.txt +0 -0
  66. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/setup.cfg +0 -0
  67. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_evidence_levels.py +0 -0
  68. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_i18n.py +0 -0
  69. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_lerobot_loader_v30_libero.py +0 -0
  70. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_lerobot_task_identity.py +0 -0
  71. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_metrics.py +0 -0
  72. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_negative_control.py +0 -0
  73. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_preflight.py +0 -0
  74. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_rda_render.py +0 -0
  75. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_recommend_offline.py +0 -0
  76. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_req10_req11_visual_calibration.py +0 -0
  77. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_req3_idle_alignment.py +0 -0
  78. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_req4_visual_integrity.py +0 -0
  79. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_validation.py +0 -0
  80. {robot_data_audit-0.7.2 → robot_data_audit-0.7.3}/tests/test_video_frame_integrity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robot-data-audit
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: Robot Data Audit (RDA) - Quality auditing tool for robot datasets
5
5
  Author-email: Niu Su Tech <dev@niusutech.com>
6
6
  License: MIT License
@@ -98,7 +98,12 @@ Optional dependency tiers:
98
98
 
99
99
  **Visual metrics without PyAV are reported as "not audited", never as
100
100
  "pass"**: the JSON report carries a top-level `skipped_by_missing_dep`
101
- field and the CLI prints a warning listing the skipped checks.
101
+ field and the CLI prints a warning listing the skipped checks. The web
102
+ dashboard shows the same guarantee — a dep-missing dataset renders a
103
+ "not audited ≠ pass" banner, and the Health Overview adds a Visual
104
+ Integrity section (checked / flagged / not-checked episode counts) while
105
+ Episode Explorer gains a per-episode Visual Audit panel (freeze-region
106
+ table, per-camera quality penalties, per-sample quality curves).
102
107
 
103
108
  ## Quick start
104
109
 
@@ -113,7 +118,7 @@ rda recommend /path/to/dataset --policy temporal # or frame-wise
113
118
  rda ui
114
119
  ```
115
120
 
116
- `rda audit` is fully offline. `rda recommend` computes all metrics locally and sends only aggregated statistics (<1KB) to the rules API — cached for offline reuse, and `RDA_API_URL` can point to your own server for private deployments.
121
+ `rda audit` is fully offline. `rda recommend` computes all metrics locally and sends only aggregated statistics (<1KB) to the rules API — cached for offline reuse, and `RDA_API_URL` can point to your own server for private deployments. The `usable_retention` statistic is **fps-aware** (v0.7.3): a "usable" run must span ≥16 frames *and* ≥1 second (DROID's `min_non_idle_len=16` encodes 1 s at 15-30 Hz; on 50 Hz data the floor rises to 50 frames so sub-second twitches no longer count).
117
122
 
118
123
  <p align="center">
119
124
  <img width="2549" alt="RDA recommendations" src="https://github.com/user-attachments/assets/e366cdf1-b169-46b0-b020-5327493a9124" />
@@ -35,7 +35,12 @@ Optional dependency tiers:
35
35
 
36
36
  **Visual metrics without PyAV are reported as "not audited", never as
37
37
  "pass"**: the JSON report carries a top-level `skipped_by_missing_dep`
38
- field and the CLI prints a warning listing the skipped checks.
38
+ field and the CLI prints a warning listing the skipped checks. The web
39
+ dashboard shows the same guarantee — a dep-missing dataset renders a
40
+ "not audited ≠ pass" banner, and the Health Overview adds a Visual
41
+ Integrity section (checked / flagged / not-checked episode counts) while
42
+ Episode Explorer gains a per-episode Visual Audit panel (freeze-region
43
+ table, per-camera quality penalties, per-sample quality curves).
39
44
 
40
45
  ## Quick start
41
46
 
@@ -50,7 +55,7 @@ rda recommend /path/to/dataset --policy temporal # or frame-wise
50
55
  rda ui
51
56
  ```
52
57
 
53
- `rda audit` is fully offline. `rda recommend` computes all metrics locally and sends only aggregated statistics (<1KB) to the rules API — cached for offline reuse, and `RDA_API_URL` can point to your own server for private deployments.
58
+ `rda audit` is fully offline. `rda recommend` computes all metrics locally and sends only aggregated statistics (<1KB) to the rules API — cached for offline reuse, and `RDA_API_URL` can point to your own server for private deployments. The `usable_retention` statistic is **fps-aware** (v0.7.3): a "usable" run must span ≥16 frames *and* ≥1 second (DROID's `min_non_idle_len=16` encodes 1 s at 15-30 Hz; on 50 Hz data the floor rises to 50 frames so sub-second twitches no longer count).
54
59
 
55
60
  <p align="center">
56
61
  <img width="2549" alt="RDA recommendations" src="https://github.com/user-attachments/assets/e366cdf1-b169-46b0-b020-5327493a9124" />
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "robot-data-audit"
7
- version = "0.7.2"
7
+ version = "0.7.3"
8
8
  description = "Robot Data Audit (RDA) - Quality auditing tool for robot datasets"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
@@ -101,11 +101,15 @@ def format_recommendation_text(result: RecommendationResult, lang: str = "zh") -
101
101
  # REQ-3 (v0.6.0): DROID-aligned retention metrics
102
102
  usable = getattr(ts, "usable_retention_ratio", {}).get("median", 0.0)
103
103
  max_idle = getattr(ts, "max_idle_run_frames", {}).get("median", 0.0)
104
+ # REQ-3② (v0.7.3): report the floor actually applied
105
+ # (max(16, 1s-in-frames) when fps is known; 16 otherwise).
106
+ floor = getattr(ts, "usable_run_floor_frames", {}).get("median", 0.0)
107
+ floor = int(round(floor)) if floor else 16
104
108
  if lang == "zh":
105
- lines.append(f" usable_retention: {usable:.1%} (连续 ≥16 帧非静止段帧占比, 对齐 DROID)")
109
+ lines.append(f" usable_retention: {usable:.1%} (连续 ≥{floor} 帧非静止段帧占比, 对齐 DROID; ≥16 帧且 ≥1s)")
106
110
  lines.append(f" max_idle_run: {max_idle:.0f} 帧 (最长连续静止段)")
107
111
  else:
108
- lines.append(f" usable_retention: {usable:.1%} (frames in runs >= 16 non-idle, DROID-aligned)")
112
+ lines.append(f" usable_retention: {usable:.1%} (frames in runs >= {floor} non-idle, DROID-aligned; >=16 frames and >=1s)")
109
113
  lines.append(f" max_idle_run: {max_idle:.0f} frames (longest static stretch)")
110
114
  else:
111
115
  if lang == "zh":
@@ -38,6 +38,9 @@ from rda.io.schema import EpisodeData
38
38
  # REQ-3 (v0.6.0): minimum length of a consecutive non-idle segment for it
39
39
  # to count as "usable" in usable_retention_ratio. Aligned with openpi's
40
40
  # DROID chunked filter (min_non_idle_len=16 ≈ 1s at typical 15-30Hz).
41
+ # REQ-3② (v0.7.3): this is the *floor*; compute_temporal_sufficiency
42
+ # raises it to max(16, fps) when the episode's fps is known, so the
43
+ # floor always means "at least 1 second" on high-fps (>16 Hz) data.
41
44
  USABLE_RUN_MIN_FRAMES = 16
42
45
 
43
46
 
@@ -228,10 +231,14 @@ class TemporalSufficiency:
228
231
  threshold: float = 0.0
229
232
  # REQ-3 (v0.6.0): DROID-aligned retention metrics.
230
233
  # usable_retention_ratio: fraction of frames inside "usable" runs —
231
- # consecutive non-idle segments of at least USABLE_RUN_MIN_FRAMES
232
- # frames (DROID min_non_idle_len=16 ≈ 1s at 15-30Hz). Directly
234
+ # consecutive non-idle segments of at least usable_run_floor_frames
235
+ # frames (REQ-3②: max(16, 1s-in-frames), DROID-aligned). Directly
233
236
  # answers "how much data survives a chunk-aligned prune".
234
237
  usable_retention_ratio: float = 0.0
238
+ # REQ-3② (v0.7.3): the actual run-length floor applied for
239
+ # usable_retention_ratio (max(16, 1s-in-frames)); lets the report
240
+ # and server-side consumers know which semantics produced the ratio.
241
+ usable_run_floor_frames: int = USABLE_RUN_MIN_FRAMES
235
242
  # Longest run of consecutive all-idle chunks (in chunks). A static
236
243
  # stretch longer than chunk_size-1 chunks cannot fit any valid
237
244
  # chunk window (DROID min_idle_len semantics).
@@ -351,12 +358,26 @@ def compute_temporal_sufficiency(
351
358
 
352
359
  # --- REQ-3 (v0.6.0): DROID-aligned retention metrics ---
353
360
  # usable_retention_ratio: frames inside active runs of at least
354
- # USABLE_RUN_MIN_FRAMES frames (DROID min_non_idle_len=16 ≈ 1s).
355
- # An active run of k steps spans k+1 frames (see runs above).
356
- usable_frames = sum(r for r in runs if r >= USABLE_RUN_MIN_FRAMES)
361
+ # MIN_USABLE_RUN frames, where MIN_USABLE_RUN = max(16, 1s in frames)
362
+ # (REQ-3②, v0.7.3): DROID's min_non_idle_len=16 encodes "≈1s at
363
+ # typical 15-30Hz". Above 16 Hz the frame floor alone undercounts
364
+ # the intended duration — at 50 Hz, 16 frames is only 0.32s, which
365
+ # lets sub-second twitches pass as "usable". Taking the max with
366
+ # 1s-in-frames restores the time-based semantics on high-fps data
367
+ # while keeping byte-identical behavior at ≤16 Hz (where 16 ≥ 1s).
368
+ # fps comes from episode.meta ("fps", set by the loader from
369
+ # info.json); fall back to the plain frame floor when unknown.
370
+ fps = episode.meta.get("fps") if episode.meta else None
371
+ try:
372
+ fps = int(fps) if fps else 0
373
+ except (TypeError, ValueError):
374
+ fps = 0
375
+ min_usable_run = max(USABLE_RUN_MIN_FRAMES, fps) if fps > 0 else USABLE_RUN_MIN_FRAMES
376
+ usable_frames = sum(r for r in runs if r >= min_usable_run)
357
377
  result.usable_retention_ratio = (
358
378
  usable_frames / T_frames if T_frames > 0 else 0.0
359
379
  )
380
+ result.usable_run_floor_frames = min_usable_run
360
381
  # max_idle_run_frames: longest run of consecutive idle steps, in
361
382
  # frames (k idle steps ≈ k+1 consecutive static frames).
362
383
  max_idle_steps = 0
@@ -431,6 +452,11 @@ class DatasetTemporalSufficiency:
431
452
  # REQ-3 (v0.6.0): DROID-aligned retention metrics (dataset level).
432
453
  usable_retention_ratio: Dict[str, float] = field(default_factory=dict)
433
454
  max_idle_run_frames: Dict[str, float] = field(default_factory=dict)
455
+ # REQ-3② (v0.7.3): median of the per-episode run-length floors that
456
+ # produced usable_retention_ratio (max(16, 1s-in-frames) when fps
457
+ # is known, else 16). Dataset-level so reports/API consumers can
458
+ # state the semantics; "unknown" floor is reported as 16.
459
+ usable_run_floor_frames: Dict[str, float] = field(default_factory=dict)
434
460
 
435
461
  def to_dict(self) -> Dict[str, Any]:
436
462
  """Serialize to dict."""
@@ -448,6 +474,7 @@ class DatasetTemporalSufficiency:
448
474
  "valid_window_ratio_10": self.valid_window_ratio_10,
449
475
  "valid_window_ratio_20": self.valid_window_ratio_20,
450
476
  "usable_retention_ratio": self.usable_retention_ratio,
477
+ "usable_run_floor_frames": self.usable_run_floor_frames,
451
478
  "max_idle_run_frames": self.max_idle_run_frames,
452
479
  }
453
480
 
@@ -519,6 +546,10 @@ def aggregate_temporal_sufficiency(
519
546
  result.usable_retention_ratio = _dist(
520
547
  np.array([ts.usable_retention_ratio for ts in valid], dtype=np.float64)
521
548
  )
549
+ # REQ-3②: dataset-level view of the run-length floor that was applied.
550
+ result.usable_run_floor_frames = _dist(
551
+ np.array([float(ts.usable_run_floor_frames) for ts in valid], dtype=np.float64)
552
+ )
522
553
  result.max_idle_run_frames = _dist(
523
554
  np.array([float(ts.max_idle_run_frames) for ts in valid], dtype=np.float64)
524
555
  )