-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Template generator #3938
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
base: main
Are you sure you want to change the base?
Template generator #3938
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -559,11 +559,15 @@ if "%arg%"=="-i" ( | |||||||
| ) else if "%arg%"=="-n" ( | ||||||||
| rem run the template generator script | ||||||||
| call :extract_python_exe | ||||||||
| rem detect non-interactive flag while reconstructing arguments | ||||||||
| set "isNonInteractive=0" | ||||||||
| set "allArgs=" | ||||||||
| set "skip=" | ||||||||
| for %%a in (%*) do ( | ||||||||
| REM Append each argument to the variable, skip the first one | ||||||||
| if defined skip ( | ||||||||
| set "allArgs=!allArgs! %%a" | ||||||||
| if /I "%%~a"=="--non-interactive" set "isNonInteractive=1" | ||||||||
| set "allArgs=!allArgs! ^"%%~a^"" | ||||||||
| ) else ( | ||||||||
| set "skip=1" | ||||||||
| ) | ||||||||
|
|
@@ -573,16 +577,24 @@ if "%arg%"=="-i" ( | |||||||
| echo. | ||||||||
| echo [INFO] Running template generator... | ||||||||
| echo. | ||||||||
| call !python_exe! tools\template\cli.py !allArgs! | ||||||||
| if "!isNonInteractive!"=="1" ( | ||||||||
| call !python_exe! tools\template\cli.py --non-interactive !allArgs! | ||||||||
| ) else ( | ||||||||
| call !python_exe! tools\template\cli.py !allArgs! | ||||||||
| ) | ||||||||
| goto :end | ||||||||
| ) else if "%arg%"=="--new" ( | ||||||||
| rem run the template generator script | ||||||||
| call :extract_python_exe | ||||||||
| rem detect non-interactive flag while reconstructing arguments | ||||||||
| set "isNonInteractive=0" | ||||||||
| set "allArgs=" | ||||||||
| set "skip=" | ||||||||
| for %%a in (%*) do ( | ||||||||
| REM Append each argument to the variable, skip the first one | ||||||||
| if defined skip ( | ||||||||
| set "allArgs=!allArgs! %%a" | ||||||||
| if /I "%%~a"=="--non-interactive" set "isNonInteractive=1" | ||||||||
| set "allArgs=!allArgs! ^"%%~a^"" | ||||||||
| ) else ( | ||||||||
| set "skip=1" | ||||||||
| ) | ||||||||
|
|
@@ -592,7 +604,11 @@ if "%arg%"=="-i" ( | |||||||
| echo. | ||||||||
| echo [INFO] Running template generator... | ||||||||
| echo. | ||||||||
| call !python_exe! tools\template\cli.py !allArgs! | ||||||||
| if "!isNonInteractive!"=="1" ( | ||||||||
| call !python_exe! tools\template\cli.py --non-interactive !allArgs! | ||||||||
|
Comment on lines
+607
to
+608
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
|
||||||||
| ) else ( | ||||||||
| call !python_exe! tools\template\cli.py !allArgs! | ||||||||
| ) | ||||||||
| goto :end | ||||||||
| ) else if "%arg%"=="-t" ( | ||||||||
| rem run the python provided by Isaac Sim | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -708,7 +708,11 @@ while [[ $# -gt 0 ]]; do | |||||||
| echo "[INFO] Installing template dependencies..." | ||||||||
| ${pip_command} -q -r ${ISAACLAB_PATH}/tools/template/requirements.txt | ||||||||
| echo -e "\n[INFO] Running template generator...\n" | ||||||||
| ${python_exe} ${ISAACLAB_PATH}/tools/template/cli.py $@ | ||||||||
| if [[ " $* " == *" --non-interactive "* ]]; then | ||||||||
| ${python_exe} ${ISAACLAB_PATH}/tools/template/cli.py --non-interactive $@ | ||||||||
|
Comment on lines
+711
to
+712
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic:
Suggested change
|
||||||||
| else | ||||||||
| ${python_exe} ${ISAACLAB_PATH}/tools/template/cli.py $@ | ||||||||
| fi | ||||||||
| # exit neatly | ||||||||
| break | ||||||||
| ;; | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| import argparse | ||
| import enum | ||
| import importlib | ||
| import os | ||
|
|
@@ -148,44 +149,79 @@ def main() -> None: | |
| """Main function to run template generation from CLI.""" | ||
| cli_handler = CLIHandler() | ||
|
|
||
| parser = argparse.ArgumentParser(add_help=True) | ||
| parser.add_argument("-n", "--non-interactive", action="store_true") | ||
| parser.add_argument("--rl-library", dest="rl_library", type=str, default=None) | ||
| parser.add_argument("--rl-algorithm", dest="rl_algorithm", type=str, default=None) | ||
| parser.add_argument( | ||
| "--task-type", | ||
| dest="task_type", | ||
| type=str, | ||
| choices=["External", "Internal", "external", "internal"], | ||
| default=None, | ||
| ) | ||
| parser.add_argument("--project-path", dest="project_path", type=str, default=None) | ||
| parser.add_argument("--project-name", dest="project_name", type=str, default=None) | ||
| parser.add_argument("--workflow", dest="workflow", type=str, default=None) | ||
| args = parser.parse_args() | ||
|
|
||
| lab_module = importlib.import_module("isaaclab") | ||
| lab_path = os.path.realpath(getattr(lab_module, "__file__", "") or (getattr(lab_module, "__path__", [""])[0])) | ||
| is_lab_pip_installed = ("site-packages" in lab_path) or ("dist-packages" in lab_path) | ||
|
|
||
| if not is_lab_pip_installed: | ||
| # project type | ||
| is_external_project = ( | ||
| cli_handler.input_select( | ||
| "Task type:", | ||
| choices=["External", "Internal"], | ||
| long_instruction=( | ||
| "External (recommended): task/project is in its own folder/repo outside the Isaac Lab project.\n" | ||
| "Internal: the task is implemented within the Isaac Lab project (in source/isaaclab_tasks)." | ||
| ), | ||
| ).lower() | ||
| == "external" | ||
| ) | ||
| if args.non_interactive: | ||
| if args.task_type is not None: | ||
| is_external_project = args.task_type.lower() == "external" | ||
| else: | ||
| is_external_project = True | ||
| else: | ||
| is_external_project = ( | ||
| cli_handler.input_select( | ||
| "Task type:", | ||
| choices=["External", "Internal"], | ||
| long_instruction=( | ||
| "External (recommended): task/project is in its own folder/repo outside the Isaac Lab" | ||
| " project.\nInternal: the task is implemented within the Isaac Lab project (in" | ||
| " source/isaaclab_tasks)." | ||
| ), | ||
| ).lower() | ||
| == "external" | ||
| ) | ||
| else: | ||
| is_external_project = True | ||
|
|
||
| # project path (if 'external') | ||
| project_path = None | ||
| if is_external_project: | ||
| project_path = cli_handler.input_path( | ||
| "Project path:", | ||
| default=os.path.dirname(ROOT_DIR) + os.sep, | ||
| validate=lambda path: not os.path.abspath(path).startswith(os.path.abspath(ROOT_DIR)), | ||
| invalid_message="External project path cannot be within the Isaac Lab project", | ||
| ) | ||
| if args.non_interactive: | ||
| project_path = args.project_path | ||
| if project_path is None: | ||
| raise SystemExit("In non-interactive mode, --project_path is required for External task type.") | ||
| if os.path.abspath(project_path).startswith(os.path.abspath(ROOT_DIR)): | ||
| raise SystemExit("External project path cannot be within the Isaac Lab project") | ||
klakhi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| else: | ||
| project_path = cli_handler.input_path( | ||
| "Project path:", | ||
| default=os.path.dirname(ROOT_DIR) + os.sep, | ||
| validate=lambda path: not os.path.abspath(path).startswith(os.path.abspath(ROOT_DIR)), | ||
| invalid_message="External project path cannot be within the Isaac Lab project", | ||
| ) | ||
|
|
||
| # project/task name | ||
| project_name = cli_handler.input_text( | ||
| "Project name:" if is_external_project else "Task's folder name:", | ||
| validate=lambda name: name.isidentifier(), | ||
| invalid_message=( | ||
| "Project/task name must be a valid identifier (Letters, numbers and underscores only. No spaces, etc.)" | ||
| ), | ||
| ) | ||
| if args.non_interactive: | ||
| project_name = args.project_name | ||
| if project_name is None or not project_name.isidentifier(): | ||
| raise SystemExit("In non-interactive mode, --project_name is required and must be a valid identifier") | ||
| else: | ||
| project_name = cli_handler.input_text( | ||
| "Project name:" if is_external_project else "Task's folder name:", | ||
| validate=lambda name: name.isidentifier(), | ||
| invalid_message=( | ||
| "Project/task name must be a valid identifier (Letters, numbers and underscores only. No spaces, etc.)" | ||
| ), | ||
| ) | ||
|
|
||
| # Isaac Lab workflow | ||
| # - show supported workflows and features | ||
|
|
@@ -199,10 +235,23 @@ def main() -> None: | |
| cli_handler.output_table(workflow_table) | ||
| # - prompt for workflows | ||
| supported_workflows = ["Direct | single-agent", "Direct | multi-agent", "Manager-based | single-agent"] | ||
| workflow = cli_handler.get_choices( | ||
| cli_handler.input_checkbox("Isaac Lab workflow:", choices=[*supported_workflows, "---", "all"]), | ||
| default=supported_workflows, | ||
| ) | ||
| if args.non_interactive: | ||
| if args.workflow is not None: | ||
| selected_workflows = [item.strip() for item in args.workflow.split(",") if item.strip()] | ||
| if any(item.lower() == "all" for item in selected_workflows): | ||
| workflow = supported_workflows | ||
| else: | ||
| selected_workflows = [item for item in selected_workflows if item in supported_workflows] | ||
| if not selected_workflows: | ||
| raise SystemExit("No valid --workflow provided for the selected workflows") | ||
| workflow = selected_workflows | ||
| else: | ||
| workflow = supported_workflows | ||
| else: | ||
| workflow = cli_handler.get_choices( | ||
| cli_handler.input_checkbox("Isaac Lab workflow:", choices=[*supported_workflows, "---", "all"]), | ||
| default=supported_workflows, | ||
| ) | ||
| workflow = [{"name": item.split(" | ")[0].lower(), "type": item.split(" | ")[1].lower()} for item in workflow] | ||
| single_agent_workflow = [item for item in workflow if item["type"] == "single-agent"] | ||
| multi_agent_workflow = [item for item in workflow if item["type"] == "multi-agent"] | ||
|
|
@@ -233,20 +282,48 @@ def main() -> None: | |
| cli_handler.output_table(rl_library_table) | ||
| # - prompt for RL libraries | ||
| supported_rl_libraries = ["rl_games", "rsl_rl", "skrl", "sb3"] if len(single_agent_workflow) else ["skrl"] | ||
| selected_rl_libraries = cli_handler.get_choices( | ||
| cli_handler.input_checkbox("RL library:", choices=[*supported_rl_libraries, "---", "all"]), | ||
| default=supported_rl_libraries, | ||
| ) | ||
| if args.non_interactive: | ||
| if args.rl_library is not None: | ||
| selected_rl_libraries_raw = [item.strip() for item in args.rl_library.split(",") if item.strip()] | ||
| if any(item.lower() == "all" for item in selected_rl_libraries_raw): | ||
| selected_rl_libraries = supported_rl_libraries | ||
| else: | ||
| selected_rl_libraries = [item for item in selected_rl_libraries_raw if item in supported_rl_libraries] | ||
| if not selected_rl_libraries: | ||
| raise SystemExit("No valid --rl_library provided for the selected workflows") | ||
| else: | ||
| selected_rl_libraries = supported_rl_libraries | ||
| else: | ||
| selected_rl_libraries = cli_handler.get_choices( | ||
| cli_handler.input_checkbox("RL library:", choices=[*supported_rl_libraries, "---", "all"]), | ||
| default=supported_rl_libraries, | ||
| ) | ||
| # - prompt for algorithms per RL library | ||
| algorithms_per_rl_library = get_algorithms_per_rl_library(len(single_agent_workflow), len(multi_agent_workflow)) | ||
| algorithms_per_rl_library = get_algorithms_per_rl_library( | ||
| bool(len(single_agent_workflow)), bool(len(multi_agent_workflow)) | ||
| ) | ||
| for rl_library in selected_rl_libraries: | ||
| algorithms = algorithms_per_rl_library.get(rl_library, []) | ||
| if len(algorithms) > 1: | ||
| algorithms = cli_handler.get_choices( | ||
| cli_handler.input_checkbox(f"RL algorithms for {rl_library}:", choices=[*algorithms, "---", "all"]), | ||
| default=algorithms, | ||
| ) | ||
| rl_library_algorithms.append({"name": rl_library, "algorithms": [item.lower() for item in algorithms]}) | ||
| if args.non_interactive: | ||
| if args.rl_algorithm is not None: | ||
| provided_algorithms = [item.strip().lower() for item in args.rl_algorithm.split(",") if item.strip()] | ||
| if "all" in provided_algorithms: | ||
| selected_algorithms = [item.lower() for item in algorithms] | ||
| else: | ||
| valid_algorithms = [item for item in provided_algorithms if item in [a.lower() for a in algorithms]] | ||
| if not valid_algorithms: | ||
| raise SystemExit(f"No valid --rl_algorithm provided for library '{rl_library}'") | ||
| selected_algorithms = valid_algorithms | ||
| else: | ||
| selected_algorithms = [item.lower() for item in algorithms] | ||
|
Comment on lines
+307
to
+318
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. logic: Same When multiple RL libraries are selected, the same Example: User passes |
||
| else: | ||
| if len(algorithms) > 1: | ||
| algorithms = cli_handler.get_choices( | ||
| cli_handler.input_checkbox(f"RL algorithms for {rl_library}:", choices=[*algorithms, "---", "all"]), | ||
| default=algorithms, | ||
| ) | ||
| selected_algorithms = [item.lower() for item in algorithms] | ||
| rl_library_algorithms.append({"name": rl_library, "algorithms": selected_algorithms}) | ||
|
|
||
| specification = { | ||
| "external": is_external_project, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic:
--non-interactiveflag is passed twice when already present in!allArgs!