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,136 @@
|
|
|
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
|
+
from collections import defaultdict
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
|
|
19
|
+
from simple_parsing import field
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Cmd:
|
|
24
|
+
"""List command configuration."""
|
|
25
|
+
|
|
26
|
+
json: bool = field(
|
|
27
|
+
default=False,
|
|
28
|
+
action="store_true",
|
|
29
|
+
help="Print output as JSON instead of table format",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def execute(self) -> None:
|
|
33
|
+
# Import heavy dependencies only when needed
|
|
34
|
+
import json
|
|
35
|
+
|
|
36
|
+
from nemo_evaluator_launcher.api.functional import get_tasks_list
|
|
37
|
+
|
|
38
|
+
# TODO(dfridman): modify `get_tasks_list` to return a list of dicts in the first place
|
|
39
|
+
data = get_tasks_list()
|
|
40
|
+
headers = ["task", "endpoint_type", "harness", "container"]
|
|
41
|
+
supported_benchmarks = []
|
|
42
|
+
for task_data in data:
|
|
43
|
+
assert len(task_data) == len(headers)
|
|
44
|
+
supported_benchmarks.append(dict(zip(headers, task_data)))
|
|
45
|
+
|
|
46
|
+
if self.json:
|
|
47
|
+
print(json.dumps({"tasks": supported_benchmarks}, indent=2))
|
|
48
|
+
else:
|
|
49
|
+
self._print_table(supported_benchmarks)
|
|
50
|
+
|
|
51
|
+
def _print_table(self, tasks: list[dict]) -> None:
|
|
52
|
+
"""Print tasks grouped by harness and container in table format."""
|
|
53
|
+
if not tasks:
|
|
54
|
+
print("No tasks found.")
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
# Group tasks by harness and container
|
|
58
|
+
grouped = defaultdict(lambda: defaultdict(list))
|
|
59
|
+
for task in tasks:
|
|
60
|
+
harness = task["harness"]
|
|
61
|
+
container = task["container"]
|
|
62
|
+
grouped[harness][container].append(task)
|
|
63
|
+
|
|
64
|
+
# Print grouped tables
|
|
65
|
+
for i, (harness, containers) in enumerate(grouped.items()):
|
|
66
|
+
if i > 0:
|
|
67
|
+
print() # Extra spacing between harnesses
|
|
68
|
+
|
|
69
|
+
for j, (container, container_tasks) in enumerate(containers.items()):
|
|
70
|
+
if j > 0:
|
|
71
|
+
print() # Spacing between containers
|
|
72
|
+
|
|
73
|
+
# Prepare task table first to get column widths
|
|
74
|
+
task_headers = ["task", "endpoint_type"]
|
|
75
|
+
rows = []
|
|
76
|
+
for task in container_tasks:
|
|
77
|
+
rows.append([task["task"], task["endpoint_type"]])
|
|
78
|
+
|
|
79
|
+
# Sort tasks alphabetically for better readability
|
|
80
|
+
rows.sort(key=lambda x: x[0])
|
|
81
|
+
|
|
82
|
+
# Calculate column widths with some padding
|
|
83
|
+
widths = [
|
|
84
|
+
max(len(task_headers[i]), max(len(str(row[i])) for row in rows)) + 2
|
|
85
|
+
for i in range(len(task_headers))
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
# Calculate minimum table width based on task columns
|
|
89
|
+
min_table_width = sum(widths) + len(widths) + 1
|
|
90
|
+
|
|
91
|
+
# Calculate required width for header content
|
|
92
|
+
harness_line = f"harness: {harness}"
|
|
93
|
+
container_line = f"container: {container}"
|
|
94
|
+
header_content_width = (
|
|
95
|
+
max(len(harness_line), len(container_line)) + 4
|
|
96
|
+
) # +4 for "| " and " |"
|
|
97
|
+
|
|
98
|
+
# Use the larger of the two widths
|
|
99
|
+
table_width = max(min_table_width, header_content_width)
|
|
100
|
+
|
|
101
|
+
# Print combined header with harness and container info
|
|
102
|
+
print("=" * table_width)
|
|
103
|
+
print(f"{harness_line}")
|
|
104
|
+
print(f"{container_line}")
|
|
105
|
+
|
|
106
|
+
# Adjust column widths to fill the full table width
|
|
107
|
+
available_width = table_width
|
|
108
|
+
# Give more space to the first column (task names can be long)
|
|
109
|
+
adjusted_widths = [
|
|
110
|
+
max(
|
|
111
|
+
widths[0], available_width * 2 // 3
|
|
112
|
+
), # 2/3 of available width for task
|
|
113
|
+
0, # Will be calculated as remainder
|
|
114
|
+
]
|
|
115
|
+
adjusted_widths[1] = (
|
|
116
|
+
available_width - adjusted_widths[0]
|
|
117
|
+
) # Remainder for endpoint_type
|
|
118
|
+
|
|
119
|
+
# Print task table header separator
|
|
120
|
+
print(" " * table_width)
|
|
121
|
+
header_row = f"{task_headers[0]:<{adjusted_widths[0]}}{task_headers[1]:<{adjusted_widths[1]}}"
|
|
122
|
+
print(header_row)
|
|
123
|
+
print("-" * table_width)
|
|
124
|
+
|
|
125
|
+
# Print task rows
|
|
126
|
+
for row in rows:
|
|
127
|
+
data_row = f"{str(row[0]):<{adjusted_widths[0]}}{str(row[1]):<{adjusted_widths[1]}}"
|
|
128
|
+
print(data_row)
|
|
129
|
+
|
|
130
|
+
print("-" * table_width)
|
|
131
|
+
# Show task count
|
|
132
|
+
task_count = len(rows)
|
|
133
|
+
print(f" {task_count} task{'s' if task_count != 1 else ''} available")
|
|
134
|
+
print("=" * table_width)
|
|
135
|
+
|
|
136
|
+
print()
|
|
@@ -0,0 +1,226 @@
|
|
|
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
|
+
"""Main CLI module using simple-parsing with subcommands."""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
from simple_parsing import ArgumentParser
|
|
21
|
+
|
|
22
|
+
import nemo_evaluator_launcher.cli.export as export
|
|
23
|
+
import nemo_evaluator_launcher.cli.info as info
|
|
24
|
+
import nemo_evaluator_launcher.cli.kill as kill
|
|
25
|
+
import nemo_evaluator_launcher.cli.ls_runs as ls_runs
|
|
26
|
+
import nemo_evaluator_launcher.cli.ls_tasks as ls_tasks
|
|
27
|
+
import nemo_evaluator_launcher.cli.run as run
|
|
28
|
+
import nemo_evaluator_launcher.cli.status as status
|
|
29
|
+
import nemo_evaluator_launcher.cli.version as version
|
|
30
|
+
from nemo_evaluator_launcher.common.logging_utils import logger
|
|
31
|
+
|
|
32
|
+
VERSION_HELP = "Show version information"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_verbose_enabled(args) -> bool:
|
|
36
|
+
"""Check if verbose flag is enabled in any subcommand."""
|
|
37
|
+
# Check global verbose flag
|
|
38
|
+
if hasattr(args, "verbose") and args.verbose:
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
# Check subcommand verbose flags
|
|
42
|
+
subcommands = [
|
|
43
|
+
"run",
|
|
44
|
+
"status",
|
|
45
|
+
"info",
|
|
46
|
+
"kill",
|
|
47
|
+
"tasks_alias",
|
|
48
|
+
"tasks",
|
|
49
|
+
"runs",
|
|
50
|
+
"export",
|
|
51
|
+
]
|
|
52
|
+
for subcmd in subcommands:
|
|
53
|
+
if hasattr(args, subcmd) and hasattr(getattr(args, subcmd), "verbose"):
|
|
54
|
+
if getattr(getattr(args, subcmd), "verbose"):
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def create_parser() -> ArgumentParser:
|
|
61
|
+
"""Create and configure the CLI argument parser with subcommands."""
|
|
62
|
+
parser = ArgumentParser()
|
|
63
|
+
|
|
64
|
+
# Add --version flag at the top level
|
|
65
|
+
parser.add_argument("--version", action="store_true", help=VERSION_HELP)
|
|
66
|
+
|
|
67
|
+
# Add --verbose/-v flag for debug logging
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"-v",
|
|
70
|
+
"--verbose",
|
|
71
|
+
action="store_true",
|
|
72
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
subparsers = parser.add_subparsers(dest="command", required=False)
|
|
76
|
+
|
|
77
|
+
# Version subcommand
|
|
78
|
+
version_parser = subparsers.add_parser(
|
|
79
|
+
"version",
|
|
80
|
+
help=VERSION_HELP,
|
|
81
|
+
description=VERSION_HELP,
|
|
82
|
+
)
|
|
83
|
+
version_parser.add_arguments(version.Cmd, dest="version")
|
|
84
|
+
|
|
85
|
+
# Run subcommand
|
|
86
|
+
run_parser = subparsers.add_parser(
|
|
87
|
+
"run", help="Run evaluation", description="Run evaluation"
|
|
88
|
+
)
|
|
89
|
+
run_parser.add_argument(
|
|
90
|
+
"-v",
|
|
91
|
+
"--verbose",
|
|
92
|
+
action="store_true",
|
|
93
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
94
|
+
)
|
|
95
|
+
run_parser.add_arguments(run.Cmd, dest="run")
|
|
96
|
+
|
|
97
|
+
# Status subcommand
|
|
98
|
+
status_parser = subparsers.add_parser(
|
|
99
|
+
"status", help="Check job status", description="Check job status"
|
|
100
|
+
)
|
|
101
|
+
status_parser.add_argument(
|
|
102
|
+
"-v",
|
|
103
|
+
"--verbose",
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
106
|
+
)
|
|
107
|
+
status_parser.add_arguments(status.Cmd, dest="status")
|
|
108
|
+
|
|
109
|
+
# Kill subcommand
|
|
110
|
+
kill_parser = subparsers.add_parser(
|
|
111
|
+
"kill",
|
|
112
|
+
help="Kill a job or invocation",
|
|
113
|
+
description="Kill a job (e.g., aefc4819.0) or entire invocation (e.g., aefc4819) by its ID",
|
|
114
|
+
)
|
|
115
|
+
kill_parser.add_argument(
|
|
116
|
+
"-v",
|
|
117
|
+
"--verbose",
|
|
118
|
+
action="store_true",
|
|
119
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
120
|
+
)
|
|
121
|
+
kill_parser.add_arguments(kill.Cmd, dest="kill")
|
|
122
|
+
|
|
123
|
+
# Ls subcommand (with nested subcommands)
|
|
124
|
+
ls_parser = subparsers.add_parser(
|
|
125
|
+
"ls", help="List resources", description="List tasks or runs"
|
|
126
|
+
)
|
|
127
|
+
ls_parser.add_argument(
|
|
128
|
+
"-v",
|
|
129
|
+
"--verbose",
|
|
130
|
+
action="store_true",
|
|
131
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
132
|
+
)
|
|
133
|
+
# Add arguments from `ls tasks` so that they work with `ls` as default alias
|
|
134
|
+
ls_parser.add_arguments(ls_tasks.Cmd, dest="tasks_alias")
|
|
135
|
+
|
|
136
|
+
ls_sub = ls_parser.add_subparsers(dest="ls_command", required=False)
|
|
137
|
+
|
|
138
|
+
# ls tasks (default)
|
|
139
|
+
ls_tasks_parser = ls_sub.add_parser(
|
|
140
|
+
"tasks", help="List available tasks", description="List available tasks"
|
|
141
|
+
)
|
|
142
|
+
ls_tasks_parser.add_arguments(ls_tasks.Cmd, dest="tasks")
|
|
143
|
+
|
|
144
|
+
# ls runs (invocations summary)
|
|
145
|
+
ls_runs_parser = ls_sub.add_parser(
|
|
146
|
+
"runs",
|
|
147
|
+
help="List invocations (runs)",
|
|
148
|
+
description="Show a concise table of invocations from the exec DB",
|
|
149
|
+
)
|
|
150
|
+
ls_runs_parser.add_arguments(ls_runs.Cmd, dest="runs")
|
|
151
|
+
|
|
152
|
+
# Export subcommand
|
|
153
|
+
export_parser = subparsers.add_parser(
|
|
154
|
+
"export",
|
|
155
|
+
help="Export evaluation results",
|
|
156
|
+
description="Export evaluation results takes a List of invocation ids and a list of destinations(local, gitlab, wandb)",
|
|
157
|
+
)
|
|
158
|
+
export_parser.add_argument(
|
|
159
|
+
"-v",
|
|
160
|
+
"--verbose",
|
|
161
|
+
action="store_true",
|
|
162
|
+
help="Enable verbose logging (sets LOG_LEVEL=DEBUG)",
|
|
163
|
+
)
|
|
164
|
+
export_parser.add_arguments(export.ExportCmd, dest="export")
|
|
165
|
+
|
|
166
|
+
# Info subcommand
|
|
167
|
+
info_parser = subparsers.add_parser(
|
|
168
|
+
"info",
|
|
169
|
+
help="Display evaluation job information",
|
|
170
|
+
description="Info functionalities for nemo-evaluator-launcher",
|
|
171
|
+
)
|
|
172
|
+
info_parser.add_argument(
|
|
173
|
+
"-v", "--verbose", action="store_true", help="Enable verbose logging"
|
|
174
|
+
)
|
|
175
|
+
info_parser.add_arguments(info.InfoCmd, dest="info")
|
|
176
|
+
|
|
177
|
+
return parser
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def main() -> None:
|
|
181
|
+
"""Main CLI entry point with subcommands."""
|
|
182
|
+
parser = create_parser()
|
|
183
|
+
args = parser.parse_args()
|
|
184
|
+
|
|
185
|
+
# Handle --verbose flag
|
|
186
|
+
if is_verbose_enabled(args):
|
|
187
|
+
os.environ["LOG_LEVEL"] = "DEBUG"
|
|
188
|
+
|
|
189
|
+
# Handle --version flag
|
|
190
|
+
if hasattr(args, "version") and args.version:
|
|
191
|
+
version_cmd = version.Cmd()
|
|
192
|
+
version_cmd.execute()
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Handle case where no command is provided but --version wasn't used
|
|
196
|
+
if not hasattr(args, "command") or args.command is None:
|
|
197
|
+
parser.print_help()
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
logger.debug("Parsed arguments", args=args)
|
|
201
|
+
if args.command == "version":
|
|
202
|
+
args.version.execute()
|
|
203
|
+
elif args.command == "run":
|
|
204
|
+
args.run.execute()
|
|
205
|
+
elif args.command == "status":
|
|
206
|
+
args.status.execute()
|
|
207
|
+
elif args.command == "kill":
|
|
208
|
+
args.kill.execute()
|
|
209
|
+
elif args.command == "ls":
|
|
210
|
+
# Dispatch nested ls subcommands
|
|
211
|
+
if args.ls_command is None or args.ls_command == "tasks":
|
|
212
|
+
# Default to tasks when no subcommand specified
|
|
213
|
+
if hasattr(args, "tasks_alias"):
|
|
214
|
+
args.tasks_alias.execute()
|
|
215
|
+
else:
|
|
216
|
+
args.tasks.execute()
|
|
217
|
+
elif args.ls_command == "runs":
|
|
218
|
+
args.runs.execute()
|
|
219
|
+
elif args.command == "export":
|
|
220
|
+
args.export.execute()
|
|
221
|
+
elif args.command == "info":
|
|
222
|
+
args.info.execute()
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
main()
|
|
@@ -0,0 +1,200 @@
|
|
|
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
|
+
import pathlib
|
|
17
|
+
import time
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
from simple_parsing import field
|
|
21
|
+
|
|
22
|
+
from nemo_evaluator_launcher.common.logging_utils import logger
|
|
23
|
+
from nemo_evaluator_launcher.common.printing_utils import (
|
|
24
|
+
bold,
|
|
25
|
+
cyan,
|
|
26
|
+
green,
|
|
27
|
+
magenta,
|
|
28
|
+
red,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class Cmd:
|
|
34
|
+
"""Run command parameters"""
|
|
35
|
+
|
|
36
|
+
config_name: str = field(
|
|
37
|
+
default="default",
|
|
38
|
+
alias=["-c", "--config-name"],
|
|
39
|
+
metadata={
|
|
40
|
+
"help": "Config name to use. Consult `nemo_evaluator_launcher.configs`"
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
config_dir: str | None = field(
|
|
44
|
+
default=None,
|
|
45
|
+
alias=["-d", "--config-dir"],
|
|
46
|
+
metadata={
|
|
47
|
+
"help": "Path to user config directory. If provided, searches here first, then falls back to internal configs."
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
run_config_file: str | None = field(
|
|
51
|
+
default=None,
|
|
52
|
+
alias=["-f", "--run-config-file"],
|
|
53
|
+
metadata={
|
|
54
|
+
"help": "Path to a run config file to load directly (bypasses Hydra config loading)."
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
override: list[str] = field(
|
|
58
|
+
default_factory=list,
|
|
59
|
+
action="append",
|
|
60
|
+
nargs="?",
|
|
61
|
+
alias=["-o"],
|
|
62
|
+
metadata={
|
|
63
|
+
"help": "Hydra override in the form some.param.path=value (pass multiple `-o` for multiple overrides).",
|
|
64
|
+
},
|
|
65
|
+
)
|
|
66
|
+
dry_run: bool = field(
|
|
67
|
+
default=False,
|
|
68
|
+
alias=["-n", "--dry-run"],
|
|
69
|
+
metadata={"help": "Do not run the evaluation, just print the config."},
|
|
70
|
+
)
|
|
71
|
+
config_output: str | None = field(
|
|
72
|
+
default=None,
|
|
73
|
+
alias=["--config-output"],
|
|
74
|
+
metadata={
|
|
75
|
+
"help": "Directory to save the complete run config. Defaults to ~/.nemo-evaluator/run_configs/"
|
|
76
|
+
},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def execute(self) -> None:
|
|
80
|
+
# Import heavy dependencies only when needed
|
|
81
|
+
import yaml
|
|
82
|
+
from omegaconf import OmegaConf
|
|
83
|
+
|
|
84
|
+
from nemo_evaluator_launcher.api.functional import RunConfig, run_eval
|
|
85
|
+
|
|
86
|
+
# Load configuration either from Hydra or from a run config file
|
|
87
|
+
if self.run_config_file:
|
|
88
|
+
# Validate that run config file is not used with other config options
|
|
89
|
+
if self.config_name != "default":
|
|
90
|
+
raise ValueError("Cannot use --run-config-file with --config-name")
|
|
91
|
+
if self.config_dir is not None:
|
|
92
|
+
raise ValueError("Cannot use --run-config-file with --config-dir")
|
|
93
|
+
if self.override:
|
|
94
|
+
raise ValueError("Cannot use --run-config-file with --override")
|
|
95
|
+
|
|
96
|
+
# Load from run config file
|
|
97
|
+
with open(self.run_config_file, "r") as f:
|
|
98
|
+
config_dict = yaml.safe_load(f)
|
|
99
|
+
|
|
100
|
+
# Create RunConfig from the loaded data
|
|
101
|
+
config = OmegaConf.create(config_dict)
|
|
102
|
+
else:
|
|
103
|
+
# Load the complete Hydra configuration
|
|
104
|
+
config = RunConfig.from_hydra(
|
|
105
|
+
config_name=self.config_name,
|
|
106
|
+
hydra_overrides=self.override,
|
|
107
|
+
config_dir=self.config_dir,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
invocation_id = run_eval(config, self.dry_run)
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(red(f"✗ Job submission failed, see logs | Error: {e}"))
|
|
114
|
+
logger.error("Job submission failed", error=e)
|
|
115
|
+
raise
|
|
116
|
+
|
|
117
|
+
# Save the complete configuration
|
|
118
|
+
if not self.dry_run and invocation_id is not None:
|
|
119
|
+
# Determine config output directory
|
|
120
|
+
if self.config_output:
|
|
121
|
+
# Use custom directory specified by --config-output
|
|
122
|
+
config_dir = pathlib.Path(self.config_output)
|
|
123
|
+
else:
|
|
124
|
+
# Default to original location: ~/.nemo-evaluator/run_configs
|
|
125
|
+
home_dir = pathlib.Path.home()
|
|
126
|
+
config_dir = home_dir / ".nemo-evaluator" / "run_configs"
|
|
127
|
+
|
|
128
|
+
# Ensure the directory exists
|
|
129
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
|
|
131
|
+
# Convert DictConfig to dict and save as YAML
|
|
132
|
+
config_dict = OmegaConf.to_container(config, resolve=True)
|
|
133
|
+
config_yaml = yaml.dump(
|
|
134
|
+
config_dict, default_flow_style=False, sort_keys=False, indent=2
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Create config filename with invocation ID
|
|
138
|
+
config_filename = f"{invocation_id}_config.yml"
|
|
139
|
+
config_path = config_dir / config_filename
|
|
140
|
+
|
|
141
|
+
# Save the complete Hydra configuration
|
|
142
|
+
with open(config_path, "w") as f:
|
|
143
|
+
f.write("# Complete configuration from nemo-evaluator-launcher\n")
|
|
144
|
+
f.write(
|
|
145
|
+
f"# Generated at: {time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime())}\n"
|
|
146
|
+
)
|
|
147
|
+
f.write(f"# Invocation ID: {invocation_id}\n")
|
|
148
|
+
f.write("#\n")
|
|
149
|
+
f.write("# This is the complete raw configuration\n")
|
|
150
|
+
f.write("#\n")
|
|
151
|
+
f.write("# To rerun this exact configuration:\n")
|
|
152
|
+
f.write(
|
|
153
|
+
f"# nemo-evaluator-launcher run --run-config-file {config_path}\n"
|
|
154
|
+
)
|
|
155
|
+
f.write("#\n")
|
|
156
|
+
f.write(config_yaml)
|
|
157
|
+
|
|
158
|
+
print(bold(cyan("Complete run config saved to: ")) + f"\n {config_path}\n")
|
|
159
|
+
logger.info("Saved complete config", path=config_path)
|
|
160
|
+
|
|
161
|
+
# Print general success message with invocation ID and helpful commands
|
|
162
|
+
if invocation_id is not None and not self.dry_run:
|
|
163
|
+
print(
|
|
164
|
+
bold(cyan("To check status: "))
|
|
165
|
+
+ f"nemo-evaluator-launcher status {invocation_id}"
|
|
166
|
+
)
|
|
167
|
+
print(
|
|
168
|
+
bold(cyan("To kill all jobs: "))
|
|
169
|
+
+ f"nemo-evaluator-launcher kill {invocation_id}"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Show actual job IDs and task names
|
|
173
|
+
print(bold(cyan("To kill individual jobs:")))
|
|
174
|
+
# Access tasks - will work after normalization in run_eval
|
|
175
|
+
tasks = (
|
|
176
|
+
config.evaluation.tasks
|
|
177
|
+
if hasattr(config.evaluation, "tasks")
|
|
178
|
+
else config.evaluation
|
|
179
|
+
)
|
|
180
|
+
for idx, task in enumerate(tasks):
|
|
181
|
+
job_id = f"{invocation_id}.{idx}"
|
|
182
|
+
print(f" nemo-evaluator-launcher kill {job_id} # {task.name}")
|
|
183
|
+
|
|
184
|
+
print(
|
|
185
|
+
magenta(
|
|
186
|
+
"(all commands accept shortened IDs as long as there are no conflicts)"
|
|
187
|
+
)
|
|
188
|
+
)
|
|
189
|
+
print(
|
|
190
|
+
bold(cyan("To print all jobs: ")) + "nemo-evaluator-launcher ls runs"
|
|
191
|
+
"\n (--since 1d or --since 6h for time span, see --help)"
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
print(
|
|
195
|
+
green(
|
|
196
|
+
bold(
|
|
197
|
+
f"✓ Job submission successful | Invocation ID: {invocation_id}"
|
|
198
|
+
)
|
|
199
|
+
)
|
|
200
|
+
)
|