nemo-evaluator-launcher 0.1.28__py3-none-any.whl
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.
Potentially problematic release.
This version of nemo-evaluator-launcher might be problematic. Click here for more details.
- nemo_evaluator_launcher/__init__.py +79 -0
- nemo_evaluator_launcher/api/__init__.py +24 -0
- nemo_evaluator_launcher/api/functional.py +698 -0
- nemo_evaluator_launcher/api/types.py +98 -0
- nemo_evaluator_launcher/api/utils.py +19 -0
- nemo_evaluator_launcher/cli/__init__.py +15 -0
- nemo_evaluator_launcher/cli/export.py +267 -0
- nemo_evaluator_launcher/cli/info.py +512 -0
- nemo_evaluator_launcher/cli/kill.py +41 -0
- nemo_evaluator_launcher/cli/ls_runs.py +134 -0
- nemo_evaluator_launcher/cli/ls_tasks.py +136 -0
- nemo_evaluator_launcher/cli/main.py +226 -0
- nemo_evaluator_launcher/cli/run.py +200 -0
- nemo_evaluator_launcher/cli/status.py +164 -0
- nemo_evaluator_launcher/cli/version.py +55 -0
- nemo_evaluator_launcher/common/__init__.py +16 -0
- nemo_evaluator_launcher/common/execdb.py +283 -0
- nemo_evaluator_launcher/common/helpers.py +366 -0
- nemo_evaluator_launcher/common/logging_utils.py +357 -0
- nemo_evaluator_launcher/common/mapping.py +295 -0
- nemo_evaluator_launcher/common/printing_utils.py +93 -0
- nemo_evaluator_launcher/configs/__init__.py +15 -0
- nemo_evaluator_launcher/configs/default.yaml +28 -0
- nemo_evaluator_launcher/configs/deployment/generic.yaml +33 -0
- nemo_evaluator_launcher/configs/deployment/nim.yaml +32 -0
- nemo_evaluator_launcher/configs/deployment/none.yaml +16 -0
- nemo_evaluator_launcher/configs/deployment/sglang.yaml +38 -0
- nemo_evaluator_launcher/configs/deployment/trtllm.yaml +24 -0
- nemo_evaluator_launcher/configs/deployment/vllm.yaml +42 -0
- nemo_evaluator_launcher/configs/execution/lepton/default.yaml +92 -0
- nemo_evaluator_launcher/configs/execution/local.yaml +19 -0
- nemo_evaluator_launcher/configs/execution/slurm/default.yaml +34 -0
- nemo_evaluator_launcher/executors/__init__.py +22 -0
- nemo_evaluator_launcher/executors/base.py +120 -0
- nemo_evaluator_launcher/executors/lepton/__init__.py +16 -0
- nemo_evaluator_launcher/executors/lepton/deployment_helpers.py +609 -0
- nemo_evaluator_launcher/executors/lepton/executor.py +1004 -0
- nemo_evaluator_launcher/executors/lepton/job_helpers.py +398 -0
- nemo_evaluator_launcher/executors/local/__init__.py +15 -0
- nemo_evaluator_launcher/executors/local/executor.py +605 -0
- nemo_evaluator_launcher/executors/local/run.template.sh +103 -0
- nemo_evaluator_launcher/executors/registry.py +38 -0
- nemo_evaluator_launcher/executors/slurm/__init__.py +15 -0
- nemo_evaluator_launcher/executors/slurm/executor.py +1147 -0
- nemo_evaluator_launcher/exporters/__init__.py +36 -0
- nemo_evaluator_launcher/exporters/base.py +121 -0
- nemo_evaluator_launcher/exporters/gsheets.py +409 -0
- nemo_evaluator_launcher/exporters/local.py +502 -0
- nemo_evaluator_launcher/exporters/mlflow.py +619 -0
- nemo_evaluator_launcher/exporters/registry.py +40 -0
- nemo_evaluator_launcher/exporters/utils.py +624 -0
- nemo_evaluator_launcher/exporters/wandb.py +490 -0
- nemo_evaluator_launcher/package_info.py +38 -0
- nemo_evaluator_launcher/resources/mapping.toml +380 -0
- nemo_evaluator_launcher-0.1.28.dist-info/METADATA +494 -0
- nemo_evaluator_launcher-0.1.28.dist-info/RECORD +60 -0
- nemo_evaluator_launcher-0.1.28.dist-info/WHEEL +5 -0
- nemo_evaluator_launcher-0.1.28.dist-info/entry_points.txt +3 -0
- nemo_evaluator_launcher-0.1.28.dist-info/licenses/LICENSE +451 -0
- nemo_evaluator_launcher-0.1.28.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""Type definitions for the nemo-evaluator-launcher public API.
|
|
17
|
+
|
|
18
|
+
This module defines data structures and helpers for configuration and type safety in the API layer.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import warnings
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
from typing import cast
|
|
25
|
+
|
|
26
|
+
# ruff: noqa: E402
|
|
27
|
+
# Later when adding optional module to hydra, since the internal package is optional,
|
|
28
|
+
# will generate a hydra warning. We suppress it as distraction and bad UX, before hydra gets invoked.
|
|
29
|
+
warnings.filterwarnings(
|
|
30
|
+
"ignore",
|
|
31
|
+
message="provider=hydra.searchpath.*path=nemo_evaluator_launcher_internal.*is not available\\.",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
import hydra
|
|
35
|
+
from hydra.core.global_hydra import GlobalHydra
|
|
36
|
+
from omegaconf import DictConfig, OmegaConf
|
|
37
|
+
|
|
38
|
+
from nemo_evaluator_launcher.common.logging_utils import logger
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class RunConfig(DictConfig):
|
|
43
|
+
@staticmethod
|
|
44
|
+
def from_hydra(
|
|
45
|
+
config_name: str = "default",
|
|
46
|
+
config_dir: str | None = None,
|
|
47
|
+
hydra_overrides: list[str] = [],
|
|
48
|
+
dict_overrides: dict = {},
|
|
49
|
+
) -> "RunConfig":
|
|
50
|
+
"""Load configuration from Hydra and merge with dictionary overrides.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
config_name: Name of the Hydra configuration to load.
|
|
54
|
+
hydra_overrides: List of Hydra command-line style overrides.
|
|
55
|
+
dict_overrides: Dictionary of configuration overrides to merge.
|
|
56
|
+
config_dir: Optional path to user config directory. If provided, Hydra will
|
|
57
|
+
search in this directory first, then fall back to internal configs.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
RunConfig: Merged configuration object.
|
|
61
|
+
"""
|
|
62
|
+
overrides = hydra_overrides.copy()
|
|
63
|
+
# Check if a GlobalHydra instance is already initialized and clear it
|
|
64
|
+
if GlobalHydra.instance().is_initialized():
|
|
65
|
+
GlobalHydra.instance().clear()
|
|
66
|
+
|
|
67
|
+
if config_dir:
|
|
68
|
+
# Convert relative path to absolute path if needed
|
|
69
|
+
if not os.path.isabs(config_dir):
|
|
70
|
+
config_dir = os.path.abspath(config_dir)
|
|
71
|
+
|
|
72
|
+
hydra.initialize_config_dir(
|
|
73
|
+
config_dir=config_dir,
|
|
74
|
+
version_base=None,
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
hydra.initialize_config_module(
|
|
78
|
+
config_module="nemo_evaluator_launcher.configs",
|
|
79
|
+
version_base=None,
|
|
80
|
+
)
|
|
81
|
+
overrides = overrides + [
|
|
82
|
+
"hydra.searchpath=[pkg://nemo_evaluator_launcher.configs,pkg://nemo_evaluator_launcher_internal.configs]"
|
|
83
|
+
]
|
|
84
|
+
cfg = hydra.compose(config_name=config_name, overrides=overrides)
|
|
85
|
+
|
|
86
|
+
# Merge dict_overrides if provided
|
|
87
|
+
if dict_overrides:
|
|
88
|
+
cfg = OmegaConf.merge(cfg, dict_overrides)
|
|
89
|
+
|
|
90
|
+
logger.debug(
|
|
91
|
+
"Loaded run config from hydra",
|
|
92
|
+
config_name=config_name,
|
|
93
|
+
config_dir=config_dir,
|
|
94
|
+
overrides=hydra_overrides,
|
|
95
|
+
dict_overrides=dict_overrides,
|
|
96
|
+
result=cfg,
|
|
97
|
+
)
|
|
98
|
+
return cast("RunConfig", cfg)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""Configuration validation utilities for nemo-evaluator-launcher.
|
|
17
|
+
|
|
18
|
+
This module provides helper functions to validate configuration objects.
|
|
19
|
+
"""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""Export evaluation results to specified target."""
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Any, List, Optional
|
|
20
|
+
|
|
21
|
+
from simple_parsing import field
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ExportCmd:
|
|
26
|
+
"""Export evaluation results."""
|
|
27
|
+
|
|
28
|
+
# Short usage examples will show up in -h as the class docstring:
|
|
29
|
+
# Examples:
|
|
30
|
+
# nemo-evaluator-launcher export 8abcd123 --dest local --format json --out .
|
|
31
|
+
# nemo-evaluator-launcher export 8abcd123.0 9ef01234 --dest local --format csv --out results/ -fname processed_results.csv
|
|
32
|
+
# nemo-evaluator-launcher export 8abcd123 --dest jet
|
|
33
|
+
|
|
34
|
+
invocation_ids: List[str] = field(
|
|
35
|
+
positional=True,
|
|
36
|
+
help="IDs to export (space-separated). Accepts invocation IDs (xxxxxxxx) and job IDs (xxxxxxxx.n); mixture of both allowed.",
|
|
37
|
+
)
|
|
38
|
+
dest: str = field(
|
|
39
|
+
default="local",
|
|
40
|
+
alias=["--dest"],
|
|
41
|
+
choices=["local", "wandb", "mlflow", "gsheets", "jet"],
|
|
42
|
+
help="Export destination.",
|
|
43
|
+
)
|
|
44
|
+
# overrides for exporter config; use -o similar to run command
|
|
45
|
+
override: List[str] = field(
|
|
46
|
+
default_factory=list,
|
|
47
|
+
action="append",
|
|
48
|
+
nargs="?",
|
|
49
|
+
alias=["-o", "--override"],
|
|
50
|
+
help="Hydra-style overrides for exporter config. Use `export.<dest>.key=value` (e.g., -o export.wandb.entity=org-name).",
|
|
51
|
+
)
|
|
52
|
+
output_dir: Optional[str] = field(
|
|
53
|
+
default=".",
|
|
54
|
+
alias=["--output-dir", "-out"],
|
|
55
|
+
help="Output directory (default: current directory).",
|
|
56
|
+
)
|
|
57
|
+
output_filename: Optional[str] = field(
|
|
58
|
+
default=None,
|
|
59
|
+
alias=["--output-filename", "-fname"],
|
|
60
|
+
help="Summary filename (default: processed_results.json/csv based on --format).",
|
|
61
|
+
)
|
|
62
|
+
format: Optional[str] = field(
|
|
63
|
+
default=None,
|
|
64
|
+
alias=["--format"],
|
|
65
|
+
choices=["json", "csv"],
|
|
66
|
+
help="Summary format for --dest local. Omit to only copy artifacts.",
|
|
67
|
+
)
|
|
68
|
+
copy_logs: bool = field(
|
|
69
|
+
default=False,
|
|
70
|
+
alias=["--copy-logs"],
|
|
71
|
+
help="Include logs when copying locally (default: False).",
|
|
72
|
+
)
|
|
73
|
+
log_metrics: List[str] = field(
|
|
74
|
+
default_factory=list,
|
|
75
|
+
alias=["--log-metrics"],
|
|
76
|
+
help="Filter metrics by name (repeatable). Examples: score, f1, mmlu_score_micro.",
|
|
77
|
+
)
|
|
78
|
+
only_required: Optional[bool] = field(
|
|
79
|
+
default=None,
|
|
80
|
+
alias=["--only-required"],
|
|
81
|
+
help="Copy only required+optional artifacts (default: True). Set to False to copy all available artifacts.",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def execute(self) -> None:
|
|
85
|
+
"""Execute export."""
|
|
86
|
+
# Import heavy dependencies only when needed
|
|
87
|
+
from omegaconf import OmegaConf
|
|
88
|
+
|
|
89
|
+
from nemo_evaluator_launcher.api.functional import export_results
|
|
90
|
+
|
|
91
|
+
# Validation: ensure IDs are provided
|
|
92
|
+
if not self.invocation_ids:
|
|
93
|
+
print("Error: No IDs provided. Specify one or more invocation or job IDs.")
|
|
94
|
+
print(
|
|
95
|
+
"Usage: nemo-evaluator-launcher export <id> [<id>...] --dest <destination>"
|
|
96
|
+
)
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
config: dict[str, Any] = {
|
|
100
|
+
"copy_logs": self.copy_logs,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Output handling
|
|
104
|
+
if self.output_dir:
|
|
105
|
+
config["output_dir"] = self.output_dir
|
|
106
|
+
if self.output_filename:
|
|
107
|
+
config["output_filename"] = self.output_filename
|
|
108
|
+
|
|
109
|
+
# Format and filters
|
|
110
|
+
if self.format:
|
|
111
|
+
config["format"] = self.format
|
|
112
|
+
if self.log_metrics:
|
|
113
|
+
config["log_metrics"] = self.log_metrics
|
|
114
|
+
|
|
115
|
+
# Add only_required if explicitly passed via CLI
|
|
116
|
+
if self.only_required is not None:
|
|
117
|
+
config["only_required"] = self.only_required
|
|
118
|
+
|
|
119
|
+
# Parse and validate overrides
|
|
120
|
+
if self.override:
|
|
121
|
+
# Flatten possible list-of-lists from parser
|
|
122
|
+
flat_overrides: list[str] = []
|
|
123
|
+
for item in self.override:
|
|
124
|
+
if isinstance(item, list):
|
|
125
|
+
flat_overrides.extend(str(x) for x in item)
|
|
126
|
+
else:
|
|
127
|
+
flat_overrides.append(str(item))
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
self._validate_overrides(flat_overrides, self.dest)
|
|
131
|
+
except ValueError as e:
|
|
132
|
+
print(f"Error: {e}")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
# Expand env vars in override vals ($VAR / ${VAR})
|
|
136
|
+
import os
|
|
137
|
+
|
|
138
|
+
from omegaconf import OmegaConf
|
|
139
|
+
|
|
140
|
+
expanded_overrides: list[str] = []
|
|
141
|
+
for ov in flat_overrides:
|
|
142
|
+
if "=" in ov:
|
|
143
|
+
k, v = ov.split("=", 1)
|
|
144
|
+
expanded_overrides.append(f"{k}={os.path.expandvars(v)}")
|
|
145
|
+
else:
|
|
146
|
+
expanded_overrides.append(os.path.expandvars(ov))
|
|
147
|
+
|
|
148
|
+
dot_cfg = OmegaConf.from_dotlist(expanded_overrides)
|
|
149
|
+
as_dict = OmegaConf.to_container(dot_cfg, resolve=True) or {}
|
|
150
|
+
if isinstance(as_dict, dict) and "export" in as_dict:
|
|
151
|
+
export_map = as_dict.get("export") or {}
|
|
152
|
+
if isinstance(export_map, dict) and self.dest in export_map:
|
|
153
|
+
config.update(export_map[self.dest] or {})
|
|
154
|
+
else:
|
|
155
|
+
config.update(as_dict)
|
|
156
|
+
else:
|
|
157
|
+
config.update(as_dict)
|
|
158
|
+
|
|
159
|
+
if self.format and self.dest != "local":
|
|
160
|
+
print(
|
|
161
|
+
"Note: --format is only used by --dest local. It will be ignored for other destinations."
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if "only_required" in config and self.only_required is True:
|
|
165
|
+
config.pop("only_required", None)
|
|
166
|
+
|
|
167
|
+
print(
|
|
168
|
+
f"Exporting {len(self.invocation_ids)} {'invocations' if len(self.invocation_ids) > 1 else 'invocation'} to {self.dest}..."
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
result = export_results(self.invocation_ids, self.dest, config)
|
|
172
|
+
|
|
173
|
+
if not result.get("success", False):
|
|
174
|
+
err = result.get("error", "Unknown error")
|
|
175
|
+
print(f"\nExport failed: {err}")
|
|
176
|
+
# Provide actionable guidance for common configuration issues
|
|
177
|
+
if self.dest == "mlflow":
|
|
178
|
+
if "tracking_uri" in str(err).lower():
|
|
179
|
+
print("\nMLflow requires 'tracking_uri' to be configured.")
|
|
180
|
+
print(
|
|
181
|
+
"Set it via: -o export.mlflow.tracking_uri=http://mlflow-server:5000"
|
|
182
|
+
)
|
|
183
|
+
elif "not installed" in str(err).lower():
|
|
184
|
+
print("\nMLflow package not installed.")
|
|
185
|
+
print("Install via: pip install nemo-evaluator-launcher[mlflow]")
|
|
186
|
+
elif self.dest == "wandb":
|
|
187
|
+
if "entity" in str(err).lower() or "project" in str(err).lower():
|
|
188
|
+
print("\nW&B requires 'entity' and 'project' to be configured.")
|
|
189
|
+
print(
|
|
190
|
+
"Set via: -o export.wandb.entity=my-org -o export.wandb.project=my-proj"
|
|
191
|
+
)
|
|
192
|
+
elif "not installed" in str(err).lower():
|
|
193
|
+
print("\nW&B package not installed.")
|
|
194
|
+
print("Install via: pip install nemo-evaluator-launcher[wandb]")
|
|
195
|
+
elif self.dest == "gsheets":
|
|
196
|
+
if "not installed" in str(err).lower():
|
|
197
|
+
print("\nGoogle Sheets package not installed.")
|
|
198
|
+
print("Install via: pip install nemo-evaluator-launcher[gsheets]")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# Success path
|
|
202
|
+
if len(self.invocation_ids) == 1:
|
|
203
|
+
# Single invocation
|
|
204
|
+
invocation_id = self.invocation_ids[0]
|
|
205
|
+
print(f"Export completed for {invocation_id}")
|
|
206
|
+
|
|
207
|
+
for job_id, job_result in result["jobs"].items():
|
|
208
|
+
if job_result.get("success"):
|
|
209
|
+
print(f" {job_id}: {job_result.get('message', '')}")
|
|
210
|
+
metadata = job_result.get("metadata", {})
|
|
211
|
+
if metadata.get("run_url"):
|
|
212
|
+
print(f" URL: {metadata['run_url']}")
|
|
213
|
+
if metadata.get("summary_path"):
|
|
214
|
+
print(f" Summary: {metadata['summary_path']}")
|
|
215
|
+
path_hint = job_result.get("dest") or metadata.get("output_dir")
|
|
216
|
+
if self.dest == "local" and path_hint:
|
|
217
|
+
print(f" Path: {path_hint}")
|
|
218
|
+
else:
|
|
219
|
+
print(f" {job_id} failed: {job_result.get('message', '')}")
|
|
220
|
+
else:
|
|
221
|
+
# Multiple invocations
|
|
222
|
+
metadata = result.get("metadata", {})
|
|
223
|
+
print(
|
|
224
|
+
f"Export completed: {metadata.get('successful_invocations', 0)}/{metadata.get('total_invocations', 0)} successful"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Show summary path if available
|
|
228
|
+
if metadata.get("summary_path"):
|
|
229
|
+
print(f"Summary: {metadata['summary_path']}")
|
|
230
|
+
# Show per-invocation status
|
|
231
|
+
for invocation_id, inv_result in result["invocations"].items():
|
|
232
|
+
if inv_result.get("success"):
|
|
233
|
+
job_count = len(inv_result.get("jobs", {}))
|
|
234
|
+
print(f" {invocation_id}: {job_count} jobs")
|
|
235
|
+
else:
|
|
236
|
+
print(
|
|
237
|
+
f" {invocation_id}: failed, {inv_result.get('error', 'Unknown error')}"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def _validate_overrides(self, overrides: List[str], dest: str) -> None:
|
|
241
|
+
"""Validate override list for destination consistency.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
ValueError: If overrides specify wrong destination or have other issues.
|
|
245
|
+
"""
|
|
246
|
+
if not overrides:
|
|
247
|
+
return # nothing to validate
|
|
248
|
+
|
|
249
|
+
# Check each override for destination mismatch
|
|
250
|
+
for override_str in overrides:
|
|
251
|
+
if override_str.startswith(
|
|
252
|
+
"export."
|
|
253
|
+
): # check if override starts with export.
|
|
254
|
+
# Extract destination from override path
|
|
255
|
+
try:
|
|
256
|
+
key_part = override_str.split("=")[0] # Get left side before =
|
|
257
|
+
parts = key_part.split(".")
|
|
258
|
+
if len(parts) >= 2:
|
|
259
|
+
override_dest = parts[1]
|
|
260
|
+
if override_dest != dest:
|
|
261
|
+
raise ValueError(
|
|
262
|
+
f"Override destination mismatch: override specifies 'export.{override_dest}' but --dest is '{dest}'. "
|
|
263
|
+
f"Either change --dest to '{override_dest}' or use 'export.{dest}' in overrides."
|
|
264
|
+
)
|
|
265
|
+
except (IndexError, AttributeError):
|
|
266
|
+
# miconstructed override -> OmegaConf handles this
|
|
267
|
+
pass
|