Repository navigation
[API] Run, get, list experiments #1063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
e60e47d
Add path-based experiment execution API
podkidyshev eb304dc
Keep legacy run signatures in CLI compatibility shims
podkidyshev 749d08a
Add experiment lookup to Python API
podkidyshev c129fad
Simplify API experiment discovery
podkidyshev b0a8617
Constrain experiment lookup to result directories
podkidyshev f0ed6e5
Keep output directory override in CLI
podkidyshev 2afa177
Support run modes in Python API
podkidyshev 19c315e
Split Python API tests by function
podkidyshev 123bcb2
Report experiment startup through Python API
podkidyshev 060a571
Expose experiment cancellation on startup
podkidyshev 870a074
Register CLI signals after experiment startup
podkidyshev c526768
Use consistent handlers imports in CLI
podkidyshev c9513e6
Allow explicit runner class overrides
podkidyshev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| Python API | ||
| ========== | ||
|
|
||
| ``cloudai.api`` lets a Python program run a scenario and find its saved results. | ||
| Pass configuration file paths as ``pathlib.Path`` objects: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import cloudai.api | ||
|
|
||
| system = Path("conf/common/system/example_slurm_cluster.toml") | ||
| scenario = Path("conf/common/test_scenario/sleep.toml") | ||
|
|
||
| experiment = cloudai.api.run_experiment( | ||
| scenario, system, tests_dir=Path("conf/common/test") | ||
| ) | ||
| print(experiment.status, experiment.path) | ||
|
|
||
| ``run_experiment`` waits for the scenario to finish and returns an ``Experiment`` | ||
| model with the same data saved in ``experiment.json``. Workload failures appear in | ||
| its status. Configuration and setup errors raise exceptions. Use | ||
| ``mode="dry-run"`` to generate commands without running workloads. Use | ||
| ``single_sbatch=True`` for a single Slurm allocation. Pass ``hook_dir`` to use a | ||
| custom hook directory. Pass an ``on_start`` callback to receive the initial | ||
| ``Experiment`` and a cancellation function after ``experiment.json`` is created | ||
| and before execution begins. The function remains synchronous after calling the | ||
| callback. | ||
|
|
||
| ``list_experiments(system)`` returns ``(experiment_id, result_directory)`` pairs | ||
| from the system's configured output directory. Directories without | ||
| ``experiment.json`` are skipped. A missing output directory gives an empty list; | ||
| experiment file contents are not parsed. | ||
|
|
||
| ``get_experiment(experiment_id, system)`` loads an ``Experiment`` from that | ||
| system's output directory. Pass a ``Path`` to a result directory to load it | ||
| directly. Invalid IDs and missing, unreadable, or malformed experiment files | ||
| raise ``ValueError``. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES | ||
| # Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import collections.abc | ||
| import pathlib | ||
|
|
||
| import pydantic | ||
|
|
||
| import cloudai.core | ||
| import cloudai.handlers | ||
| import cloudai.models.output | ||
|
|
||
|
|
||
| def run_experiment( | ||
| scenario: pathlib.Path, | ||
| system: pathlib.Path, | ||
| *, | ||
| mode: str = "run", | ||
| single_sbatch: bool = False, | ||
| tests_dir: pathlib.Path | None = None, | ||
| hook_dir: pathlib.Path | None = None, | ||
| on_start: ( | ||
| collections.abc.Callable[[cloudai.models.output.Experiment, collections.abc.Callable[[], None]], None] | None | ||
| ) = None, | ||
| ) -> cloudai.models.output.Experiment: | ||
| """ | ||
| Run a scenario synchronously and return its experiment snapshot. | ||
|
|
||
| Configuration and setup errors raise exceptions. Workload failures are | ||
| reflected in the returned experiment status. | ||
| """ | ||
| parsed_system, tests, parsed_scenario = cloudai.handlers.load_experiment( | ||
| scenario.expanduser().resolve(), | ||
| system.expanduser().resolve(), | ||
| tests_dir=tests_dir, | ||
| hook_dir=hook_dir, | ||
| ) | ||
| runner = cloudai.handlers.create_experiment_runner( | ||
| parsed_system, | ||
| parsed_scenario, | ||
| mode=mode, | ||
| single_sbatch=single_sbatch, | ||
| ) | ||
| cloudai.handlers.execute_experiment(runner, tests, on_start=on_start) | ||
| return runner.runner.experiment_output.snapshot() | ||
|
|
||
|
|
||
| def list_experiments(system: pathlib.Path) -> list[tuple[str, pathlib.Path]]: | ||
| """List saved experiment IDs and result directories for a system.""" | ||
| output_dir = cloudai.core.Parser.parse_system(system.expanduser().resolve()).output_path.expanduser().resolve() | ||
| if not output_dir.exists(): | ||
| return [] | ||
| if not output_dir.is_dir(): | ||
| raise NotADirectoryError(output_dir) | ||
|
|
||
| return [ | ||
| (experiment_file.parent.name, experiment_file.parent) | ||
| for experiment_file in sorted(output_dir.glob("*/experiment.json")) | ||
| if experiment_file.is_file() | ||
| ] | ||
|
|
||
|
|
||
| def get_experiment(exp_id: str | pathlib.Path, system: str | pathlib.Path) -> cloudai.models.output.Experiment: | ||
| """Load an experiment by ID or result directory.""" | ||
| parsed_system = cloudai.core.Parser.parse_system(pathlib.Path(system).expanduser().resolve()) | ||
| system_output = parsed_system.output_path.expanduser().resolve() | ||
|
|
||
| experiment_path = system_output / exp_id if isinstance(exp_id, str) else exp_id | ||
| experiment_path = experiment_path.expanduser().resolve() | ||
| experiment_file = experiment_path / "experiment.json" | ||
| if experiment_path.parent != system_output or not experiment_path.is_dir() or not experiment_file.is_file(): | ||
| raise ValueError(f"Invalid experiment ID: {experiment_path!r}") | ||
|
|
||
| try: | ||
| experiment_data = experiment_file.read_text(encoding="utf-8") | ||
| except (OSError, ValueError) as error: | ||
| raise ValueError(f"Invalid experiment ID: {experiment_path!r}") from error | ||
|
|
||
| try: | ||
| exp = cloudai.models.output.Experiment.model_validate_json(experiment_data) | ||
| except pydantic.ValidationError as error: | ||
| raise ValueError(f"Cannot read experiment: {experiment_path!r}") from error | ||
|
|
||
| return exp |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.