Repository navigation
Write KPIs as InfluxDB-ready records - #1541
david-tingdahl-nvidia wants to merge 1 commit into
Conversation
|
/review |
[Medium impact] This PR appears safe to merge; no actionable issues were found. SummaryAdds flat KPI records for experiments, Runs, and timers, then writes them after the report in both output paths.
Diagram%%{init: {'theme': 'neutral'}}%%
flowchart LR
R[Experiment result] --> K[Build KPI records]
E[Episode results] --> S[Pool completed Run episodes]
S --> K
T[Optional timing file] --> K
K --> V[Check values and unique tags]
V --> J[Write KPI JSON]
Reviews (1) · Last reviewed commit: "evaluation: write the experiment KPIs as..." · Reviewed by Greptile |
| timings_path = experiment_output_directory / ARENA_AGGREGATED_TIMINGS_FILENAME | ||
| if not timings_path.is_file(): | ||
| return [] | ||
| assert ALL_RUNS_TIMING_RUN_NAME not in run_names, ( |
There was a problem hiding this comment.
🟡 A Run named all fails only after the whole Experiment has run
Nothing rejects a Run named all up front. experiment_runner.py (line 200) and build_experiment_output.py (line 187) always write the timings file before this step, so this assert fires after every Run has finished. On OSMO it is worse: the per-Run runner exits non-zero, ExperimentRunnerTask records the Run as failed, and the collector drops its episodes. Then the collector's own KPI step hits the same assert and fails the task under set -euo pipefail.
Could the totals get their own record type, for example type="timing_total" with no run_name tag? Then no Run name could collide with them. If you'd rather keep "all", could this check move next to assert_run_name_is_safe_path_component so it fails before any Run starts?
| report_path = build_report(experiment_output_directory) | ||
|
|
||
| # Write KPI records for InfluxDB. | ||
| write_experiment_influxdb_kpi_records(experiment_output_directory) |
There was a problem hiding this comment.
🟡 Should the runner write this by default?
Only the per-commit KPI pipeline needs this file, but every local and OSMO run now builds it. If any assert in it fires, the runner exits non-zero after the evaluation is already done.
On OSMO, that exit code turns a completed Run into a failed one (ExperimentRunnerTask._get_run_script), and the collector then drops its episodes. The file written here is thrown away anyway, because collect_run_outputs_into_experiment_output copies only <runner-output>/<run_name>/.
Could this be opt-in, for example a runner flag the KPI job sets, or left to build_experiment_output.py? build_experiment_influxdb_kpi_records only needs the output directory, so a small __main__ entry point would also let the KPI job build the file from any finished output.
|
|
||
| @dataclass(kw_only=True) | ||
| class PerExperimentRecord(InfluxdbKpiRecord): | ||
| """KPIs totalled over every Run of the Experiment.""" |
There was a problem hiding this comment.
🟡 How will a dashboard tell Experiments apart?
per_experiment records and the run_name="all" timing records only have type, git_sha and git_branch as tags. Neither ArenaExperimentCfg nor arena_experiment_result.json has an Experiment name. If two benchmarks run on the same commit, their records end up in the same series and differ only by timestamp, so a "success rate per commit" chart would mix them.
Is the ingester meant to pick a measurement per Experiment? If so, could the module docstring say that? If not, these records probably need an Experiment tag.
| import math | ||
| import time | ||
|
|
||
| MIN_TIMESTAMP_NS = 1767225600 * 10**9 |
There was a problem hiding this comment.
🔵 Second copy of the InfluxDB rules
This helper repeats MIN_TIMESTAMP_NS, TIMESTAMP_NS_KEY, _is_number, _is_finite_number and the tag-set uniqueness check from the production module. It also adds rules the production records don't check: no keys starting with _, and no time key. On top of that, it needs 17 tests of its own (test_validation_helper_rejects_invalid_records, test_validation_helper_rejects_non_list_contents).
Could those extra rules move into InfluxdbKpiRecord.__post_init__, so there is one set of rules? The integration tests could then compare the written file with the output of build_experiment_influxdb_kpi_records, as test_written_file_is_valid_influxdb_points already does. That would let you drop this helper and its tests.
| Returns: | ||
| The per-Run record. | ||
| """ | ||
| record_tags = { |
There was a problem hiding this comment.
🔵 record_tags also holds the timestamp
The module says timestamp_ns is not a tag (TIMESTAMP_NS_KEY, the skip in __post_init__), but it's in this dict. A name like shared_values would match what the dict holds. Both **record_tags uses below would need the same rename.
🤖 Isaac Lab-Arena Review BotSummaryThis PR adds Design, Boundaries & Scope
Findings🟡 Warning: isaaclab_arena/evaluation/experiment_runner.py:15 — This new top-level import now loads torch, gymnasium, Earlier inline findings on this commit are still open: Test Coverage and CI Cost
VerdictMinor fixes needed |
| load_arena_experiment_from_config_file, | ||
| validate_experiment_config_path, | ||
| ) | ||
| from isaaclab_arena.evaluation.arena_experiment_influxdb_kpi_records import write_experiment_influxdb_kpi_records |
There was a problem hiding this comment.
🟡 Loads torch and Isaac Lab before the app starts
This top-level import loads arena_run, which the KPI module imports for RunStatus. arena_run in turn imports isaaclab_arena.policy, whose __init__.py star-imports rsl_rl_action_policy and replay_action_policy. So the runner now loads torch, gymnasium, isaaclab.utils and isaaclab_rl before SimulationAppContext starts the app on line 157. Before this PR, only isaaclab.app, hydra and the standard library were loaded at that point. timer.py:147 notes that torch must not load before Isaac Sim starts.
Could this import move into the with SimulationAppContext(args_cli): block next to the build_report import, where the runner already imports arena_run? Another option: the KPI module could compare against COMPLETED_STATUS from report_data and drop its arena_run import. That would keep it as light as report_data.py.
| tmp_path / "experiment", | ||
| { | ||
| "pnp_pi05": {"episodes": _episode_records(50, 41), "environment": "droid_pnp"}, | ||
| "pnp_pi0": {"episodes": _episode_records(50, 40, episode_length=20), "policy_variant": "pi0"}, |
There was a problem hiding this comment.
🔵 Can't tell the episode-weighted rate from a plain average
Both Runs have 50 episodes, so averaging the two per-Run rates also gives 81 / 100, and this test would still pass if the code did that. If one Run has a different episode count, the test checks the episode-weighted rate that PerExperimentRecord.from_jobs documents:
| "pnp_pi0": {"episodes": _episode_records(50, 40, episode_length=20), "policy_variant": "pi0"}, | |
| "pnp_pi0": {"episodes": _episode_records(10, 2, episode_length=20), "policy_variant": "pi0"}, |
The expected values below would then change to num_successes == 2 for this Run, and to success_rate == 43 / 60 and num_episodes == 60 in the per-Experiment record.
| experiment_output_directory = _write_experiment( | ||
| tmp_path / "experiment", | ||
| { | ||
| "late": {"episodes": _episode_records(1, 1), "end_timestamp_ns": t2}, |
There was a problem hiding this comment.
🔵 Latest Run is listed first
late comes before early, so code that used the first Run's end_timestamp_ns instead of the latest would also pass. Listing early first would make the test check what its name says.
4bb7683 to
e46a6c6
Compare
cf86516 to
67102b8
Compare
Add arena_experiment_influxdb_kpi_records, which writes the Experiment's KPIs as a flat JSON list of records ready for InfluxDB ingestion, and call it from experiment_runner and the OSMO build_experiment_output script once the report is written. Signed-off-by: David Tingdahl <dtingdahl@nvidia.com>
67102b8 to
88d0c1e
Compare
Write the Experiment's KPIs as a flat list of InfluxDB-ready records.
Why this change?
We are building a KPI reporting framework. The results of an Experiment need to reach a time-series database so that
success rates and timings can be tracked per commit. The experiment result is a nested file that is not suited for
ingestion.
What changes?
arena_experiment_influxdb_kpi_records.pybuildsarena_experiment_influxdb_kpi_records.jsonnext to theexperiment result: a flat list of records with a timestamp, the Arena commit and branch, tags and numeric fields.
There are three record types: per Experiment, per Run, and timing.
not talk to any database.
Example KPI format